Add causal person fusion and MuVAP stage-3 baselines

This commit is contained in:
Xin Wang
2026-09-10 06:54:22 +08:00
parent 0534c9d3d0
commit d426b4740a
225 changed files with 27716 additions and 119 deletions

6
third_party/EgoSpeak/.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
_legacy_/
temp/
visualization/
backups/
*.egg-info

View File

@@ -0,0 +1 @@
b5bee35e796d8f2d6aa7da2bf1b07836270f8bfb

35
third_party/EgoSpeak/LSTR/.gitignore vendored Normal file
View File

@@ -0,0 +1,35 @@
*.zip
backup/
# *.out
*-log
temp/
scripts/
# Miscellaneous
*.DS_Store
*.swp
*.pyc
*.egg-info
# PyTorch models
*.pth
checkpoints/
# Dataset soft links
data/THUMOS
data/TVSeries
data/HACS
data/EasyCom
data/EGO4D
data/EGO4D
# logs
log_*
wandb/
*.egg-info
slurm-*.out
*.mp4
wandb/
git_diff/

3
third_party/EgoSpeak/LSTR/.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "external/rulstm"]
path = external/rulstm
url = git@github.com:fpv-iplab/rulstm.git

175
third_party/EgoSpeak/LSTR/LICENSE vendored Normal file
View File

@@ -0,0 +1,175 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.

9
third_party/EgoSpeak/LSTR/NOTICE vendored Normal file
View File

@@ -0,0 +1,9 @@
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
This codebase is based on the following repository:
https://github.com/amazon-science/long-short-term-transformer
The codebase is modified from the original repository to fit the needs of the LSTR model.

View File

@@ -0,0 +1,44 @@
DATA:
DATA_NAME: 'EasyCom'
INPUT:
MODALITY: 'visual+motion' # visual+motion+object
VISUAL_FEATURE: 'rgb_kinetics_resnet50'
MOTION_FEATURE: 'audio_wav2vec2' # audio
OBJECT_FEATURE: 'audio_wav2vec2'
TARGET_PERFRAME: 'target_perframe' # 3 classes
MODEL:
MODEL_NAME: 'LSTR'
FEATURE_HEAD:
LINEAR_ENABLED: True
LINEAR_OUT_FEATURES: 1024
LSTR:
NUM_HEADS: 16
DIM_FEEDFORWARD: 1024
DROPOUT: 0.2
ACTIVATION: 'relu'
LONG_MEMORY_SECONDS: 512
LONG_MEMORY_SAMPLE_RATE: 4
LONG_MEMORY_USE_PE: True
WORK_MEMORY_SECONDS: 8
WORK_MEMORY_SAMPLE_RATE: 1
ANTICIPATION_SECONDS: 2
ANTICIPATION_SAMPLE_RATE: 1
ENC_MODULE: [
[16, 1, True], [32, 2, True]
]
DEC_MODULE: [-1, 2, True]
CRITERIONS: [['MCE', {}]]
DATA_LOADER:
BATCH_SIZE: 16
NUM_WORKERS: 8
PIN_MEMORY: True
SOLVER:
NUM_EPOCHS: 50
OPTIMIZER: 'adam'
BASE_LR: 7e-05
WEIGHT_DECAY: 5e-05
SCHEDULER:
SCHEDULER_NAME: 'warmup_cosine'
WARMUP_FACTOR: 0.3
WARMUP_EPOCHS: 10.0
WARMUP_METHOD: 'linear'

View File

@@ -0,0 +1,45 @@
DATA:
DATA_NAME: 'EasyCom'
INPUT:
MODALITY: 'visual+motion' # visual+motion+object
VISUAL_FEATURE: 'rgb_kinetics_resnet50'
MOTION_FEATURE: 'audio_wav2vec2' # audio
OBJECT_FEATURE: 'audio_wav2vec2'
TARGET_PERFRAME: 'target_perframe' # 3 classes
MODEL:
CHECKPOINT: 'checkpoints/lstr_ytconv_pretrained_AV.pth'
MODEL_NAME: 'LSTR'
FEATURE_HEAD:
LINEAR_ENABLED: True
LINEAR_OUT_FEATURES: 1024
LSTR:
NUM_HEADS: 16
DIM_FEEDFORWARD: 1024
DROPOUT: 0.2
ACTIVATION: 'relu'
LONG_MEMORY_SECONDS: 512
LONG_MEMORY_SAMPLE_RATE: 4
LONG_MEMORY_USE_PE: True
WORK_MEMORY_SECONDS: 8
WORK_MEMORY_SAMPLE_RATE: 1
ANTICIPATION_SECONDS: 2
ANTICIPATION_SAMPLE_RATE: 1
ENC_MODULE: [
[16, 1, True], [32, 2, True]
]
DEC_MODULE: [-1, 2, True]
CRITERIONS: [['MCE', {}]]
DATA_LOADER:
BATCH_SIZE: 16
NUM_WORKERS: 8
PIN_MEMORY: True
SOLVER:
NUM_EPOCHS: 50
OPTIMIZER: 'adam'
BASE_LR: 7e-05
WEIGHT_DECAY: 5e-05
SCHEDULER:
SCHEDULER_NAME: 'warmup_cosine'
WARMUP_FACTOR: 0.3
WARMUP_EPOCHS: 10.0
WARMUP_METHOD: 'linear'

View File

@@ -0,0 +1,46 @@
DATA:
DATA_NAME: 'YTConv'
DATA_INFO: 'data/YTConv_info.json'
INPUT:
MODALITY: 'visual+motion' # visual+motion+object
VISUAL_FEATURE: 'rgb_kinetics_resnet50'
MOTION_FEATURE: 'audio_wav2vec2' # audio
OBJECT_FEATURE: 'audio_wav2vec2'
TARGET_PERFRAME: 'target_perframe' # 3 classes
MODEL:
MODEL_NAME: 'LSTR'
FEATURE_HEAD:
LINEAR_ENABLED: True
LINEAR_OUT_FEATURES: 1024
LSTR:
NUM_HEADS: 16
DIM_FEEDFORWARD: 1024
DROPOUT: 0.2
ACTIVATION: 'relu'
LONG_MEMORY_SECONDS: 512
LONG_MEMORY_SAMPLE_RATE: 4
LONG_MEMORY_USE_PE: True
WORK_MEMORY_SECONDS: 8
WORK_MEMORY_SAMPLE_RATE: 1
ANTICIPATION_SECONDS: 2
ANTICIPATION_SAMPLE_RATE: 1
ENC_MODULE: [
[16, 1, True], [32, 2, True]
]
DEC_MODULE: [-1, 2, True]
CRITERIONS: [['MCE', {}]]
DATA_LOADER:
BATCH_SIZE: 16
NUM_WORKERS: 8
PIN_MEMORY: True
SOLVER:
SAVE_EVERY_EPOCHS: 5
NUM_EPOCHS: 50
OPTIMIZER: 'adam'
BASE_LR: 7e-05
WEIGHT_DECAY: 5e-05
SCHEDULER:
SCHEDULER_NAME: 'warmup_cosine'
WARMUP_FACTOR: 0.3
WARMUP_EPOCHS: 10.0
WARMUP_METHOD: 'linear'

View File

@@ -0,0 +1,45 @@
DATA:
DATA_NAME: 'EGO4DSHUFFLE'
# IGNORE_INDEX: 0 # calculate mAP w/o target not speaking (background)
INPUT:
MODALITY: 'visual+motion' # audio+visual
VISUAL_FEATURE: 'rgb_kinetics_resnet50'
MOTION_FEATURE: 'audio_wav2vec2' # audio
OBJECT_FEATURE: 'rgb_kinetics_resnet50'
TARGET_PERFRAME: 'target_perframe' # 3 classes
MODEL:
MODEL_NAME: 'LSTR'
FEATURE_HEAD:
LINEAR_ENABLED: True
LINEAR_OUT_FEATURES: 1024
LSTR:
NUM_HEADS: 16
DIM_FEEDFORWARD: 1024
DROPOUT: 0.2
ACTIVATION: 'relu'
LONG_MEMORY_SECONDS: 512
LONG_MEMORY_SAMPLE_RATE: 4
LONG_MEMORY_USE_PE: True
WORK_MEMORY_SECONDS: 8
WORK_MEMORY_SAMPLE_RATE: 1
ANTICIPATION_SECONDS: 2
ANTICIPATION_SAMPLE_RATE: 1
ENC_MODULE: [
[16, 1, True], [32, 2, True]
]
DEC_MODULE: [-1, 2, True]
CRITERIONS: [['MCE', {}]]
DATA_LOADER:
BATCH_SIZE: 16
NUM_WORKERS: 8
PIN_MEMORY: True
SOLVER:
NUM_EPOCHS: 50
OPTIMIZER: 'adam'
BASE_LR: 7e-05
WEIGHT_DECAY: 5e-05
SCHEDULER:
SCHEDULER_NAME: 'warmup_cosine'
WARMUP_FACTOR: 0.3
WARMUP_EPOCHS: 10.0
WARMUP_METHOD: 'linear'

View File

@@ -0,0 +1,46 @@
DATA:
DATA_NAME: 'EGO4DSHUFFLE'
# IGNORE_INDEX: 0 # calculate mAP w/o target not speaking (background)
INPUT:
MODALITY: 'visual+motion' # audio+visual
VISUAL_FEATURE: 'rgb_kinetics_resnet50'
MOTION_FEATURE: 'audio_wav2vec2' # audio
OBJECT_FEATURE: 'rgb_kinetics_resnet50'
TARGET_PERFRAME: 'target_perframe' # 3 classes
MODEL:
CHECKPOINT: 'checkpoints/lstr_ytconv_pretrained_AV.pth'
MODEL_NAME: 'LSTR'
FEATURE_HEAD:
LINEAR_ENABLED: True
LINEAR_OUT_FEATURES: 1024
LSTR:
NUM_HEADS: 16
DIM_FEEDFORWARD: 1024
DROPOUT: 0.2
ACTIVATION: 'relu'
LONG_MEMORY_SECONDS: 512
LONG_MEMORY_SAMPLE_RATE: 4
LONG_MEMORY_USE_PE: True
WORK_MEMORY_SECONDS: 8
WORK_MEMORY_SAMPLE_RATE: 1
ANTICIPATION_SECONDS: 2
ANTICIPATION_SAMPLE_RATE: 1
ENC_MODULE: [
[16, 1, True], [32, 2, True]
]
DEC_MODULE: [-1, 2, True]
CRITERIONS: [['MCE', {}]]
DATA_LOADER:
BATCH_SIZE: 16
NUM_WORKERS: 8
PIN_MEMORY: True
SOLVER:
NUM_EPOCHS: 50
OPTIMIZER: 'adam'
BASE_LR: 7e-05
WEIGHT_DECAY: 5e-05
SCHEDULER:
SCHEDULER_NAME: 'warmup_cosine'
WARMUP_FACTOR: 0.3
WARMUP_EPOCHS: 10.0
WARMUP_METHOD: 'linear'

View File

@@ -0,0 +1,431 @@
{
"YTConv": {
"data_root": "data/YTConv",
"class_names": [
"background",
"camera wearer start to speak"
],
"num_classes": 2,
"ignore_index": -100,
"metrics": "AP",
"fps": 5,
"train_session_set": [
"m5Aoywf51L4",
"p99cIB7nHjw",
"zGgMzBfPfaI",
"O0eCtKU3FxY",
"goZQ6iOIFYo",
"9Tb-lSyhgoE",
"GBxQtGUjAMA",
"VXgt7keOC8I",
"hZ2tin2J0P4",
"Ur-P18sm4tI",
"WfYmsNBQwg8",
"AvMNaDDfrN0",
"fW7pCN3wwdo",
"xG6HBdTtS4s",
"HcDlqUeDD9Q",
"1DE3mKncHUA",
"oEMX_quSMZY",
"jmS3nWbGV5Y",
"qUJRjPRH-u4",
"aHsUgWfUcss",
"ZfdQOmJkaK0",
"4f43nYrfTnI",
"qE1FnzkNjrE",
"4NsmKhs9flk",
"3wDSJ9ABzmo",
"sY6_8QwRZsw",
"hByfAzEoeKA",
"vDEsV0AhcjM",
"XGNhX3Nu1RE",
"MkVT8K0V57A",
"yR8y-wN8Clk",
"P1dTOlO2sl8",
"6UGC_wxTeB0",
"d4Xud2Gqoxk",
"IJ1aqePculQ",
"o8Oxf5QBitQ",
"XVci_V4qxmc",
"ofDzhOS1Pv0",
"mygTXBuMRF0",
"iq6q82Jo8ZQ",
"Kzc_ekBJ4QE",
"WEeJ9B1i8HI",
"rciqpxdCc9Y",
"Rh7d2D7JVLI",
"Qkx4hSlc8jk",
"ixi7sVLFrWM",
"n96-scSyoOQ",
"YZ5xYioIQdA",
"X76QxuWm1oA",
"2I7d_LfK75Y",
"AeUWBaYFMyo",
"lsqXFgymLlI",
"Dx_0jYMWN5o",
"0raJ1-1BJ7s",
"EGX8am5hWh0",
"KjtZ_CLY1hQ",
"R4FQI2BI3hw",
"fAoqqtsNjbQ",
"l6RwyA6jUdM",
"GJW6dlagRnU",
"j_FGvmbp6aM",
"V2O15h1VrH8",
"P3c70VbDP3E",
"epAwpBdUiYU",
"6dpv_JeyL8w",
"dUKYxsvnuBM",
"L2_zn7h8sOQ",
"m09qdCkAhmY",
"7BB71tIae7A",
"6go4oViEkdc",
"MYimxshUK1c",
"v4eZXWcIRQU",
"vGI_w1Ml6S0",
"BeFndnkT00c",
"9JHRbL1jmq4",
"0rBaR5dd-Fs",
"EvhkIS_-K-w",
"pYVbLx-h5HI",
"fVciOXYDlqg",
"zWFH4N-CmDE",
"Aw4cauzfX5E",
"lxVpcQHtcWs",
"32dkF-aXgug",
"_USorgWOsUk",
"1yVMU93nmv8",
"aYkF4Ybk-l8",
"Vwt18p0BqzM",
"oOQM5Pvjut4",
"_WWSMNtNYx0",
"zzlhwg0IJ5Y",
"lJhIffkUhr8",
"PbS9nSH3mqM",
"8EGVgiFpwhg",
"5LhXpv5IFO4",
"ibD4APVzWgg",
"2nhcSQmopO4",
"gri79OfCRvo",
"lOwQN3QcRzE",
"wQyluRNtqFo",
"IZyx8zeiKB0",
"mZyNfKyzDHU",
"A9eGJWiuIKs",
"d37DZogot1w",
"7ZUSVabB6N4",
"m62GQXq-358",
"ArwPVsCZo2c",
"QK-BFkpO8s4",
"PgfX9bvmEhs",
"l1jTUhwWJYA",
"S1PGHO4evaI",
"M6uPJa5B8Ns",
"XXzz19J-1w8",
"U4uV1xmUaB0",
"skQ0OBpkKqY",
"mfW57TDpMtc",
"NAwKA75cDwE",
"eIEBI2_Y14Q",
"eWQGYpSoiYU",
"3DPIUWhYtn8",
"_elvaRJrhE0",
"PAA8IFhWJzM",
"1XDKqhq6bb0",
"ngi9gy3DzRY",
"Y0yONfuimeE",
"g9_cAgHa_4Q",
"BIG7jEw-j4w",
"TFdom0lkd-0",
"2zV2N8_DwWg",
"kTz7XB_jpB4",
"GuoglIuhNjY",
"L98U3mO38gQ",
"UfWDoRAzpC8",
"W6TakaXCIIQ",
"i6j1I6pha-M",
"issHFC8Gbio",
"4EzGoRkd6SI",
"QoS0GgF4sI4",
"A8hJg3D_TGY",
"A3N10suCRCw",
"6962tnWRsNY",
"lS4p8osXSJs",
"wbZ7ZhcZd6o",
"fsIvQvIlKwc",
"4jQSOSi2DA8",
"Vpt6tZBvXQU",
"kxOG3kpLmYw",
"Mzjlz0nq9AA",
"80cuXt2VSko",
"VvswhogSiY0",
"5Q6ky5VrOIA",
"I_XtcUpNRF8",
"QmtmTGycK9A",
"qp3x8iy7vkA",
"xGU8QDtPrQI",
"uWWUfqth_BA",
"WbM7w8r4VGQ",
"bt6Ed1qjulw",
"7vymT27scoY",
"b9AdV6oVDVg",
"nf2zqYUAOwg",
"A-LNJc-1Jc0",
"yCI-hpPiDVc",
"Uu5DME4FqIM",
"symjuH9iOTc",
"mPf2MTnTBc0",
"NDTlQ1KGvH8",
"g9vuDzFQSRU",
"B6iqnvleve0",
"9bbszAD7JL0",
"RIyMAuz7Ckg",
"mlrQHtBRMhc",
"3gaidlW7vig",
"DGdV97q9ndk",
"qthjlg0lXFg",
"gWng5YLPOow",
"iaYG-KL9XRM",
"385414AVZcA",
"7xhBnWGpI7c",
"Nh0cmSQUs-E",
"Rvtj0QHPulA",
"rAtLksYQukw",
"S9rB__zbbe0",
"SNpMVsOPfHM",
"zhF43F4kO2w",
"HOUkcInyxN0",
"x8ymkfr2Z1o",
"Ix6Khtq6O-k",
"VHs_JyPhmtE",
"zJX3byjYSMc",
"zdqX09zZNf8",
"uPwlmwNQq8Y",
"e4xNTXrk6wE",
"P2PNUWN4GMs",
"_WVHCYjHBVY",
"5Jtv1GpPvHM",
"AIZYucKOzhw",
"vbsR_VDZXh0",
"9Ew0lDHB0kM",
"ejpkPVZHuMM",
"47HlvCfubE4",
"HzPkQSIG2m0",
"e7mTRnNnOXg",
"eZHx_biEECg",
"FvP6WFKNP4Y",
"g_TJ5XFEVfY",
"8Utg2VEWsJg",
"VMdBjL-tpoo",
"zihP2Eqeqxk",
"ILVqgOlntLM",
"Z1BWQI8KPCg",
"j8TRVp-7l3Q",
"G5gJBZ-3OlY",
"o1JUlGSirSE",
"OsPdhUQ0snE",
"-5uIaW2gKWQ",
"03kiFNh-9ME",
"w5W6mgDaJJA",
"sYsgd_8AFoo",
"vLg8kVUTmWU",
"OK0J0xsuSWQ",
"d284rjoU4Cs",
"5lE6V4jrUl4",
"bS1OpC9xu48",
"ZUxpqU_J_n0",
"AUliC0c_YIQ",
"nJ58-d_viOo",
"uIy3b2kzlrM",
"vOTLfb0qjfY",
"37_7tPZjC34",
"MUwCdP5qceI",
"lYvNi_ewd8U",
"PDG7SzDBTC4",
"QHQzoHolWvI",
"YmvOEj00LB8",
"fLhkXVx7Yw0",
"Uj5nhugVgwo",
"aHbrDu2o0WY",
"yKW6vVgEPCM",
"Q0WUgyqQ-4I",
"xw3KOPJ9a3E",
"5mrTo7c3bCM",
"x0ft032W--4",
"UdQxEbrIocc",
"EHZHAFyv_Ks",
"7DojKgPRLVk",
"-1CahRD6lJ0",
"f3TuYwl7AZE",
"TgGTovCcS9Y",
"AwyrJGeIN_g",
"RbPJD4Wh-l8",
"58bFdwYkDzU",
"6gIEKGxL7zo",
"BeuzJwTuChQ",
"YebZyAzLZuc",
"9T74b8EQBVY",
"TqSVFjw1uNM",
"WUYhCmfE1a0",
"vRCVUQFUio4",
"w2t9JdMNMgQ",
"nn6-x9xXMeE",
"FDFVPzLlzFs",
"qFhvi4bH29E",
"qdpsMXLsUZM",
"JREk1QeSc9A",
"jOVEs-4r1jc",
"v4VZI9Ouzy0",
"DMg5UUvf-C4",
"26J5zRzQkOg",
"YkiJ4iJfJbw",
"_d8j8nfTEIk",
"wMNb3hQXEUE",
"1KI8ex22blk",
"eCp2Jp7MtKg",
"HNlpHBb5dC4",
"C6_pDRdIo1k",
"ZRWJJHrgP54",
"L7jfx4veKIA",
"p1a5_qltICM",
"nZ0svM8DoY0",
"miEzjCJVaZk",
"TpC2tDCxABw",
"SZmqoqCX__U",
"dZ0bYteaI2Q",
"GW-cYEzShmY",
"iUYuPhutKVw",
"97VSm6T2zMw",
"jPiQyi5jr1M",
"lrUi4nsPZ9M",
"rXLeXke-k_w",
"mEO4izUqZ7E",
"4qI1LjMXfh0",
"mVt6myGMa-0",
"VJrnhh6FqEg",
"vwCeBWbZvrE",
"c08fl8-xmVE",
"syZ3bvXIrpA",
"i3ysNaulop4",
"__t3rU0QjsE",
"jT32uQQVGvU",
"Fzvmh-R2EWk",
"QApJSKrLg6E",
"L5y3j8SdGSU",
"3t8JGblR__0",
"e1F90W40t6g",
"j6L8UIa5z28",
"Zmg2_C1FuVI",
"XIXKJXsiBFc",
"NVHB1VdWFo4",
"-x7-8f7bsl4",
"2FSxSiowkGI",
"jgFIBIeFve0",
"1BLuXK1M5CE",
"zmpx6LUT2ow",
"z-QUzJTsFro",
"0sS4v13DAK8",
"gMIBQ0mbRvM",
"9kcaEXlTB5s",
"ZYArPGMLS4k",
"tejW9GMaucA",
"z9r4bmXCWiI",
"4Zpsb46iaLc",
"52Odc8UbvC8",
"x-B6-iYDHPs",
"8GinSDc0Is8",
"5lwbp5d_w0Y",
"I4LpTQLzlHE",
"lMl6-XH7ozY",
"tOubscNbY1Q",
"7gb7AH-oWcQ",
"mDerqt9Ib-I",
"gKV3FNd9WHE",
"kH5L1E_r-3U",
"sq5TRFxVp0w",
"SrBhj7Xusk8",
"RxlxEapsz2E",
"aCD3_TziiKo",
"_HPVZOkxdkk",
"pNCjgVQ53Y8",
"iPdT5GUoGvA",
"cIrlet1oRw4",
"1dyWUMBjMwQ",
"XZQB3cq18yc",
"i75Mn_AeFBY",
"_0VwR9WPS-k",
"q1JDjgnRdGY",
"1JNEpfvRq0Y",
"2FGj6HciQZA",
"eLi59TtNwHY",
"Z3W8OST7tmA",
"F2dNYov8-xw",
"kPfyJu_CrSA",
"7I4Y2HtAwlY",
"us-andJDfWw",
"Q0Bzn1teWfo",
"pADxBfOrYeQ",
"KnA1tIR0qCE",
"lBNPfBcb6pw",
"zt9MIiBfxF8",
"FwX12hPqCws",
"6e2LXGrze2M",
"0dvclhcFjSo",
"UTkuDYOIxKo",
"4n044cOTiNg",
"VxksG15OgRY",
"4193MkWdJcQ",
"2bDX4Uzy8Wc",
"SkyBNX2auEc",
"JrSLspuJ2gk",
"GZO7V-CWSJ8",
"TYMIy4qlRzc",
"jK7q_Li7LU0",
"UiTIXDQEkPM"
],
"test_session_set": [
"DTGtC7FC4oI",
"CIWvk3gJ_7E",
"A1fZ6rlOwxk",
"cB8WCrmJEEA",
"gGQ_2Ank9-U",
"b91abcyMeNY",
"9MuaFeyczM0",
"rwZxhdK_UVU",
"hd_WbrqYnTE",
"4BWcqA9cFFk",
"MuOkgCkob6E",
"OdhFWA3Fgb0",
"Pe3ZabrUiUk",
"U0c_uv8DU08",
"kTqt_3YJBbs",
"eWXVVMi4bf8",
"RCD-vXumMIs",
"DuwiezJCZ3I",
"BNgEqjAiKU4",
"Cbn0t7eqaEs",
"D3M9Q0WwuFY",
"oO-Bv4iRZKI",
"bBvqtbZdSZQ",
"-mnoItCi32Y",
"yQPxKfz-bwI",
"ITVZ4cx5big",
"uY5_e0L7mjw",
"J_Pl9ftnNXE",
"Jt_pb40MRy4",
"E3hAj2niRNo",
"kl2xVSxVECQ",
"gzebuFs90Xg",
"6DQSIzJ9J-g",
"pJJlnryTYWg",
"X87qsfVLFoc",
"W7BIPWU1WHc",
"Oj506EVOiQc",
"DPEvWvcstxA",
"Pk16UT5-zeM",
"XMK1KEk5OdU",
"PpNm5llsEW8",
"sAp3rvablGk"
]
}
}

View File

@@ -0,0 +1,479 @@
{
"EasyCom": {
"data_root": "data/EasyCom",
"class_names": [
"background",
"camera wearer speaking",
"other people speaking"
],
"num_classes": 3,
"ignore_index": -100,
"metrics": "AP",
"fps": 5,
"train_session_set": [
"Session_7",
"Session_11",
"Session_10",
"Session_6",
"Session_9",
"Session_4",
"Session_8",
"Session_5",
"Session_12"
],
"test_session_set": [
"Session_3",
"Session_2",
"Session_1"
]
},
"EGO4DSHUFFLE": {
"data_root": "data/EGO4D",
"class_names": [
"background",
"camera wearer speak",
"other people speaking"
],
"num_classes": 3,
"ignore_index": -100,
"metrics": "AP",
"fps": 5,
"train_session_set": [
"5b8c91ca-9d16-48b7-acce-4dec221ffd71",
"0232fbc8-c9b1-405c-bd42-a9d773fd4835",
"39c74452-1957-40db-894a-b39df51e1437",
"31488ff6-05ef-4362-9786-35981b06f307",
"4decc0fa-e98e-4bf9-bbd1-d9b852b04830",
"efeaa284-2e22-44b8-bdd6-1bb4763e5b35",
"07f1486c-44a7-4c69-9532-720c81c99eb0",
"19c28cbe-21f2-4b40-a6e0-e9932b5f0605",
"1c5c0b52-1287-4df3-aa75-8ccc916b1c10",
"e92255bc-1be6-4ee9-8f8b-d1336a0e409c",
"307e8837-1fda-4cf5-bab5-d16fee20f84f",
"bb050b8a-195a-4b50-b5d0-1ed535226a39",
"ca22f118-2472-4df0-a9cd-3d70424b211b",
"08b0935e-6260-4bd6-86ca-f6fc54e388be",
"666a21c0-7469-436e-868f-64eb8022f64f",
"77e09fd6-b560-4439-addc-a1a8a3122046",
"f00bd659-56e0-41dd-8eba-c5fdb45e7232",
"c2c7f072-a02a-480d-8342-b43ffc314db9",
"ed72b048-e309-4510-b049-b0288ce4478a",
"56c5af79-f9d4-478d-96ef-6d71e0bbbdfe",
"3d8acf67-fad6-421e-841b-82b28c82f7f6",
"977ac980-f908-4c29-a2fc-dd14ec21c0bf",
"08742cb7-bb6d-47ae-af5e-3f1f306ccda6",
"f3afa5a1-8a97-4a7d-945e-1cc70931e44a",
"65172b19-a246-4dc5-98e9-1b526b00ac89",
"ee1893cb-2ff7-40e0-af8d-19518696b698",
"53144267-6da5-40d3-91f2-eaa85be367de",
"26080fb8-0588-449c-a7f2-2e0162507e02",
"c0ba997c-9ec6-4e83-ad50-a88dd677b881",
"6640a311-86c4-4de7-964c-e3a4a67a3e02",
"5f1fcf84-4001-43ec-ad88-9e06ea2f663d",
"ea5a3f9d-7b21-46d7-a17d-bcbd4955bae9",
"eb3675b8-f6a8-4ea9-82df-1080eac5b093",
"023aaf52-fbe9-4b61-af69-ed33e91f3717",
"86af705a-a326-423a-bdd9-c6734804c30c",
"55363f67-1b41-476f-a83e-7e9741786ad7",
"ce8ea1a3-65e3-4ef7-abeb-2b0bc1c84773",
"d3b7e314-f48d-4888-b496-e1eeb252cd8a",
"59000084-d286-4c83-90c8-2fbbf80d2d31",
"a47c590d-66aa-4bd1-b552-f40951ba9bf0",
"61c73721-9476-4ac2-a987-ecaf9a04dca1",
"0124376a-720d-4265-8c8c-5ff47f04d579",
"f62a8100-7ed2-492d-9709-064514d552bf",
"13d71019-9093-442b-bdb4-c79310150592",
"74e71420-9bd6-4337-9a7a-a3dcc4cab7cf",
"383466b5-cb00-4e76-9e6a-dc1b0c9b125c",
"9ef6ef95-6b97-4c78-866c-641fc7d791f5",
"ac7d3d16-d74d-4fe3-a1e0-cab0b1adf492",
"c46cbba9-2cb3-4d84-be18-71af5539ecc5",
"5eb74ea5-9b0b-484c-82b2-ee86479c5d07",
"6f80f9ae-c276-413f-accc-fad74efe8a95",
"73f99cf0-d997-4488-b6a6-993934a9fad3",
"fd50b1d6-8142-402f-88c5-7c44ca215081",
"51fee605-d077-4d78-babc-9bbcd8d898ca",
"5e3c6abc-599b-44f8-8401-d5720627bba2",
"91151b86-65b7-4854-97b0-9c5b93a251ed",
"5be9a2e2-5efc-4fc6-a65e-806e0347e19a",
"94ba75fc-8d7f-4bb4-a756-ded62a4c1761",
"c925c38c-3fe3-4eb3-9944-3fba3eaca4db",
"d97bedc8-72df-43be-a55b-4da1ae42dfd1",
"e60b110e-a9b6-42b7-8f92-082cd74c4937",
"3627996c-8188-4486-8feb-938b9b0cce2b",
"d2b674ca-9f02-46ba-820d-f0b52c76749f",
"2b90b963-e829-4787-8fcd-1a1a434ffdfd",
"ba51db0f-2bf1-4746-894a-7db0ba54e357",
"6c1bac5c-aead-40b8-9c17-be97d86b68d4",
"63483ea2-d569-45fd-82b2-f80836a8b766",
"c81eb608-6844-424e-b8b7-524ff652cf3a",
"cae5d4bf-bbf9-45dd-876b-a48579782348",
"7813b44f-04ca-4a53-868d-fe185b615b68",
"1f12c871-9b3a-4611-a2b4-9c39059052a4",
"70316529-ec02-4d26-bc68-4b436bf0030a",
"6d367aa4-56ef-4932-9d48-e7530e717ea8",
"2e651150-9338-4103-b231-2f089aa19df7",
"d2cb4045-8ef1-4288-8398-ab6d4d6a0709",
"3e4b9c29-06d2-4ade-a2be-bdec028bc68c",
"4f3c8276-99e0-4c99-9e61-5d7c06332b09",
"12634773-451f-4c82-b915-265c703ad737",
"5f615b11-2ff8-4ff1-987a-1cd61ea78dee",
"de96ee14-be42-471d-b110-aabc948260fa",
"8504aae3-4f25-4fa4-88c1-beb3fba83f13",
"d2c0f78a-978e-409f-a78f-3e40b3431909",
"0f846b5d-1cea-40b8-b18b-dc6c54544a67",
"37afa156-a068-458e-8bf7-ec6f1f2415bc",
"2d205d38-f79c-43f2-8c39-ede697065ab2",
"36e7b2ad-dddd-42fa-a427-6411ff12f9b9",
"355f8f4a-cde2-4108-821a-5ccea1d297fe",
"c37e680f-03b1-408b-9b09-1b2ae733951d",
"6958467e-388c-4f6d-a8c0-d4782166daac",
"3051e028-71f8-4f3d-a72a-010934c3566f",
"fc0f4c90-a1ee-4147-990e-de231394f484",
"c2af1389-b165-42f5-b4ee-c90402d7006b",
"1acfcabc-653a-4c03-adf7-64838d09a7b4",
"3b79017c-4d42-40fc-a1bb-4a20bc8ebca7",
"6edb7036-3369-4c03-8e3a-439ffbeb4724",
"2ac543c6-b52b-4219-b2e4-4130078c0673",
"b3ef3563-ecc0-4a15-9a7b-4feb4558d953",
"6f001eeb-fed5-43e4-bfa2-84e274cbe5cd",
"1ce2252b-31b8-4536-9902-0ba7fb039340",
"c2413391-7c1b-4fd6-8b1d-98ee7888b9f8",
"a4d53159-e2c0-4a47-9e78-af9bc9291f0e",
"66ddb7ae-90d6-4a58-9f3c-7f1d2604ecb0",
"290fc9ba-0a29-4eed-b0e0-64edc5bf54ea",
"e3a53edd-52ff-4608-a80d-77ecfbb0f961",
"50895f7f-5667-4b43-94c1-d7b4b9e238e7",
"3aea2d62-0340-4c6a-8019-3cce9117a65a",
"63ab0246-2f1c-4621-8b71-75075b30d852",
"228eb504-c1b3-43b9-818e-2f7e539a259b",
"02de9500-d574-446e-95fc-3e82c367385a",
"bd03445e-9016-423a-bb48-cd998836bbff",
"a16cb772-d0e2-4654-96d7-372c21484b90",
"0fa75089-7b28-4988-8664-a2c93542ffb0",
"fcc6af87-918f-4d0c-a0a4-8aab36891fc4",
"b9d7642e-2ced-4589-b7a6-69ad54570c4c",
"ba5b1882-c9d7-48e7-85fe-2c7b10494fac",
"92e0925c-566d-4a67-b93e-3e4b57f9b3ab",
"130a49d0-0315-4eb0-a752-a4533bd880a9",
"0fccd442-a901-4d77-aac6-dc4276cd6591",
"49c27d75-44fa-4947-899a-8d0511a10416",
"e2ffc7c9-c035-4709-97c6-3764499188a5",
"2069aea3-ac99-4b70-891e-1bfb8b15aeea",
"cdbc7718-4cb9-4e78-bbde-3d9614f8dcd1",
"32464967-b0f2-4b2e-a57c-7f774e7056a7",
"ae6466f8-1a53-4cb3-8718-96f78f391b4b",
"d66c7c66-2b5f-4e7f-bbb2-f0adae68dfcc",
"da84b37f-ec6e-414e-b52e-1231104a68e0",
"19866484-b3d8-4619-85b9-e900de7fa80d",
"c4392ca6-4706-4a96-a6b2-29e6ef544500",
"6798a65f-d79a-413b-baca-115f09caf0c4",
"b844cb2d-6428-4d5e-abac-958f2704d4c8",
"8e9c0b21-2230-4a0a-b35b-a198b90abcaa",
"3492c684-74b5-4317-9e5c-320f3ea01899",
"5beb520c-d413-4998-bd9f-7960a2baf744",
"c96fa96d-d23c-444e-bdef-481317308bb8",
"502ce8f6-bd9e-4e40-a5c9-40bdce36b87f",
"221fd24b-ba2e-40d5-a0c7-a879ece6b34f",
"fb62dc3f-a1cd-4f0e-88bc-504fe9d8585d",
"e9acbb7f-c45c-47d7-9dd8-16194319fc97",
"0d4efcc9-a336-46f9-a1db-0623b5be2869",
"84a388b6-2844-421e-a176-84d29958a76c",
"3ecc3e4d-5466-4243-acca-2feea47e09a2",
"e7b6b4ee-3f2a-41cd-bc18-c7e993cb413f",
"5998ece4-98cd-4d5b-9104-1517d13728bd",
"c9f84bf4-108c-4151-85c0-e87375c53b91",
"5eaa3bfb-cc71-43fa-88dc-d367ef929a7b",
"0fe736da-48f1-4961-bd7a-b75ce13c91b4",
"2c7796e3-b1e9-413f-a0e8-8225ac7d586e",
"69006355-3fb0-443c-826b-ea8c9d27f70b",
"8d73dd20-5349-477f-9630-218e6b08bb95",
"e7633ad6-e406-4a18-b1ba-6c834ec9c047",
"3b3a4043-3d74-4575-8aeb-511c4a879241",
"5daa132e-0b97-4d0f-be9d-b514285661fe",
"52fb1a73-2ba7-413d-8596-33615daecc73",
"9f3cb8b5-5d62-4236-be5e-8f16d59c9f50",
"efdc3dc0-275d-4073-9179-32b14b224654",
"683635b7-5a1b-467c-8d4c-c34e8bf5fdf5",
"2db7496c-aa43-4d9d-8df6-404d2d9bd810",
"9f3edf40-3dca-4086-b995-4985f6848a14",
"ef37e1e6-5be3-4e8c-9f70-c84f341f9aea",
"5df66408-7a8c-43bb-9235-d088aa3d19dc",
"453f43e9-c1d1-4749-9720-73546287b82f",
"1943bfb1-ba78-4a6c-ade7-11e44314dfb4",
"fd8cc20a-915e-45a9-93df-320ad998aee4",
"bc6cf777-fd8a-4427-9867-92a443826827",
"812f0590-4ccd-4c9e-8978-c45e6debce55",
"e6f4e36f-96b7-48f5-afd1-27cc408bdd07",
"1b6dfc9f-c873-48c6-9f25-a2d490e269cf",
"bdf1ca5a-321e-4d3c-ac9e-da2ce1f5a69c",
"604bf883-ce55-4c59-bcc1-3ceea1128211",
"679f2016-4802-4bf8-a830-d1f45fdfcd71",
"6f3f70b4-8428-4e5f-b1dd-86cd55d8e63f",
"83d8be38-ce02-41b9-b444-7291bad513d7",
"7fd21591-6a33-49cf-a32f-f8dfdf5ae697",
"a3dbec39-7126-4949-a916-eca55fee2b49",
"03cebfe9-3319-45b5-aba5-e7db10ea6fdb",
"ff9fe54b-a959-4d7e-a6fa-93e311a68168",
"172e1afa-5581-455d-9a27-590e9a1706c2",
"3ab3b82f-bfa8-4b39-a9a9-e397341ae572",
"b171dcaf-94f2-4bcd-8695-b2648bed60e1",
"063f5040-9e94-42ae-b0e5-b13413e0fe55",
"136cef9c-e17f-49f9-ae45-a17e7e5c5477",
"f906b093-281f-4872-8010-f1f3a320ee2a",
"33365c93-7f1d-4a7d-a9bc-b33010aeafbd",
"e98405ee-0a5b-4df2-bb7e-97729b8b3c11",
"bd13e435-1e38-4b22-bfd0-b620fc74cc8a",
"7dd0eab1-b83e-4bb4-ad91-7bbdf4592647",
"5f5ea963-4f59-4705-95fe-4a98c9fcd12c",
"7e6dfd31-8544-4fad-9e49-0f05516cf8cf",
"f817b176-2dce-4858-999f-fe62aac25e2e",
"83553642-1a38-4c54-8154-c4cd4fd81173",
"dfe025f3-573e-4297-b17a-30e990cfa310",
"2091513c-8f4c-4817-8c22-cdbc6303a331",
"d15eb34f-e425-4990-aa2d-2dc454fb4b20",
"0e519838-1e4a-41fd-ab30-34bf745a3339",
"c920e557-141f-4556-95ac-f494ecc0c84f",
"91e97b3c-c912-401a-ab99-da01b90682aa",
"d6adb1ae-f689-438b-ab81-8a2e37f9654e",
"cd3092e1-6022-4a11-bfc3-a4bb80487ece",
"a874ef10-022c-43e4-b4e6-e01d8fcec988",
"2e6ff051-0037-4001-90a8-de7643a96f08",
"134c4387-77d5-4ee0-911f-e50e64499b42",
"421f1c8b-5237-4735-9003-82021ba651b1",
"9ee0474c-615c-4383-a047-8432b8b24784",
"78efcc13-3718-4acb-acee-8157a54501d6",
"961a8de1-0d33-44ab-a9d5-3d92de13fde4",
"49e25cbd-1aaa-40dd-901b-bb3e524ebcc0",
"5a5b8c37-e3f6-48f2-96d3-289a043eaee4",
"394d1df6-bced-4f04-8c2a-a1ea3a04b984",
"b87c3e0f-a5af-4663-a87d-d0689d73343b",
"09194996-ff4b-4a4f-9800-e8aedc9be7f2",
"3f920762-f4a7-4bdb-86dc-05a3c3c8e875",
"5204624f-90ac-47bf-a71a-486d43f4351c",
"df24897d-a478-4fbb-816f-1d14c59da30d",
"9ba7c2a0-c86f-4967-9284-9be88ad3eacf",
"3f28dd4f-f653-4937-83b6-9f80c1284a85",
"b2735994-59ae-4478-9c5f-93f5ccf18c8b",
"5e965191-bcd1-491a-a9ce-e014e970020b",
"85718cf6-e4ba-44e3-880f-143c627f12ff",
"d0af657d-9c21-447c-a4f9-199b38deac5b",
"f8289825-2858-4dfa-b173-f4c506359158",
"8f2d26e6-239e-492f-aa1b-472f8ec85050",
"836be9c2-cee6-4fb9-8bbd-74197d213c5a",
"cd998593-e5bc-49d0-86a7-333acf9abf93",
"8deeeb88-19ed-4747-9c79-d94104bed7e0",
"6d1dc078-8b04-480e-a36f-298594ee9592",
"83464c62-929f-4bf7-9a04-43071189edde",
"1efd8b7b-65f9-4e73-bd49-d707190d6519",
"f0b69458-25df-4dda-be8b-e14383a57a50",
"88260ff4-be0b-4544-98ef-78b6a0d61ebc",
"39fb2989-1dd2-490d-b793-dce86c36b43a",
"b4f650b4-53d6-4069-a55f-43f2de7ff47b",
"15b6759d-2370-43ee-9a61-09e453db2469",
"2e4e0dac-ab21-49ff-9563-e320acd73f2f",
"986c4e43-ac2e-4983-8d56-f1dd34775133",
"12c1b77c-cbea-4c20-8b64-7154a25bbbe1",
"a3da62c3-3f46-499f-a4c6-acf93bd1c63d",
"57b6455e-2acb-4076-b8c4-1f2cdae97185",
"d6d8261e-0ee0-4002-bddf-e94fdc1075c7",
"56683d7b-7c65-4f42-b5a4-818f3cd5224c",
"9090779d-803e-4fbd-9166-d9e90ff2d36f",
"d5374572-5576-4f83-99c8-a0b12c4de160",
"ffddab3a-dbec-49b0-b114-5a1e3d805efa",
"38c02527-1b2f-458b-bad8-73a282c1d99a",
"d612edfb-e904-4de0-abce-a5fd9ecc449b",
"1261ee8c-80fc-4a3f-9dd3-29f4a918a9fe",
"8e860a91-0205-447b-a706-aa894d23d3db",
"fb07f205-868e-44c9-a704-305daaf03301",
"48f7028c-b6bf-4ca5-81e9-128147fd02c9",
"c6a6e1e4-24c4-422d-9d18-f062240ddf23",
"ba280b86-22a2-4509-b16d-b55e04680461",
"f0cb79ef-c081-4049-85ef-2623e02c9589",
"6dbfc053-7899-40d8-9827-0ccd21f3ee0a",
"50d3035c-2503-4bcf-b28b-e66c92c98f09",
"41f7665a-a692-42e0-b28d-9769c03ab99f",
"cb1f70b4-0bf6-47f1-bc98-2e9365a55c3f",
"7f566780-4ed2-41c5-9faa-06759495faab",
"0e08e1c7-3add-4e41-b28d-cd5f4413a422",
"672e0d75-df0e-4715-8c42-f1131b059a6c",
"8f15aab0-9c42-452e-b78a-47a48da1f3c1",
"edaa0b1a-d504-4852-8025-4f12b4d293ac",
"4a9ada82-9f86-495e-b17f-25b39bef2297",
"07774a7c-c5ed-4133-991f-926a316068a4",
"5a009338-0aff-4313-b02f-c48630ce1702",
"8324c0ba-7a10-42e1-90fd-afb4d3afa789",
"3db37314-adaa-4a4e-af11-c9190fa6b65b",
"b21b35d2-6d69-40a2-8c36-aa27e4a980ae",
"6daa9124-d894-42e5-8d98-2e0e6d1191ac",
"8a256d7f-2ea1-4b0e-94ea-168a1ed85678",
"08b6f5b3-4471-4cfd-a95e-4901cb1ded57",
"ea9d85e7-6a9a-451a-b019-972a6891c11b",
"f84fd67e-0797-44f4-b3ff-78da646362e2",
"ad1a9ea9-d1ab-474a-b715-1a42d892ef5d",
"ffb191f8-894a-40e8-baa5-5578aa6ba706",
"fd394f00-4049-49e5-9c07-ee3e86979003",
"9767a3ca-0a9c-480f-adfe-e4164c52c760",
"95cdb269-0088-4451-9a0d-f72349081fab",
"4ade0a82-16fd-471f-9fcc-fd2724887aef",
"fe69a78e-7773-45d1-9e0f-bacee52dac83",
"3aed7bbd-75f0-45ef-b9cc-7f5ff6f7fbbf",
"f8d71e9a-982a-437c-a968-867149c24eb7",
"8351998e-ead7-4bc1-b0a7-a7209da92b3c",
"3eb00a40-880a-4ad9-8cec-3ba0245ec472",
"602b224a-f5ac-491b-88e3-49043f362b6e",
"89125bc3-a413-44ee-a7e1-2753c511ecab",
"00792fa8-988c-4c85-8e80-73eb3ac53e80",
"094d1ab2-f34e-4a2a-ac3b-8671ce5faf65",
"4cf10990-13e5-4a1e-a913-7ec4b8c8ab02",
"a9b191de-d565-4981-8f68-02069a2ba16c",
"0eeab9d2-a55c-4708-94c3-cc1e08f9f262",
"4e78f728-06f6-43a3-ac8c-c0cb159e5b32",
"be99e9e2-82f7-4f1f-aff5-371834fde0da",
"5682f9aa-bdfb-409c-b4c2-c0105b35ee78",
"3ce15244-5293-4a5e-8c92-52ab29c6949a",
"b4b2c55b-c107-468f-bbdb-f73a0e4dca34",
"8a409551-657a-48c0-8beb-056c228ebca5",
"3c17a762-c839-492f-b361-009c1187ae4c",
"22172001-0e17-40db-bcce-825e19532297",
"5b99234b-b940-405d-9123-70218bda24b2",
"f7869fd8-9af3-493a-a627-a3e95e7c3577",
"057e9f41-e6ed-4575-906c-aca9413b35a5",
"70e88e82-5c06-44fd-959c-d4b2d40999cd",
"b95bd349-11bb-4ade-9cc6-3be8dcea06dd",
"19b880da-910e-40c7-970e-cd8718f2faf5",
"e05a2182-a97f-4d0b-87e4-66b0dd405e58",
"3072481a-04fe-4959-8410-018a41a91df1",
"fc2b2014-9dc4-4a5d-8a1d-25a6911bff7c",
"db1d3def-397e-470f-811a-85c4cc5338a9",
"ff283659-4a79-4642-8078-4366df38e1f1",
"bc8bae7f-ad9f-4f72-ae5c-b3e7ca8dd8f0",
"a3bd1be3-edfe-4f4e-89a8-dd56ce6ece08",
"2360312c-4347-476a-990f-2633b489fa7a",
"b9e32e51-8c18-420b-a582-200c79b8208d",
"0511dce9-c126-4a11-92ba-8082a4ec89bd",
"224d8b17-7eaf-4529-9b75-c6ce3078d640",
"10732476-f045-4feb-8199-e4e04685f7ca",
"5753e3fe-f1c5-426b-9f45-5a8efa6b5370",
"b69564f5-5e5e-4097-aebd-9f52f211ff75",
"97eb574d-d8bf-44a8-a6ef-1204b789db43",
"85506322-449a-45c0-b77a-48a8077b4bbd",
"ed65870d-ba08-4894-9cad-7f34e2c6f9e4",
"cf682058-962f-45c7-95ab-dbbd2f87acc0",
"65229ac2-9033-4002-af6c-492fe49bc563",
"4c85a780-8a8e-46c9-a0f8-c7e9703ef998",
"1ac6e060-a6d2-4b42-afef-599be78a533a",
"a22beaf0-377c-4b95-b472-867280a525ae",
"f5bc6a45-1591-42bf-8ed4-8286382b31bf",
"06592f50-7f29-4ca9-94a9-1b6c389d50ea",
"4ca3d9cf-4972-4e1d-aca3-28b036d6bdc5",
"076bdb81-5c75-4282-9f3a-a387624575f3",
"a5fe8210-1c37-4dd9-bd27-74051c521c3d",
"c1ca7c5c-2473-4b83-a507-7d187609111a",
"7c4079e7-4d68-4b0d-b015-5971a9866c60",
"ead54fb5-dee9-452f-8145-227ccd2405e2",
"7c776700-1d6b-4ee2-8155-b7f6855afceb",
"71a7456a-da6e-4e38-86ec-76bacaa6db5b",
"26c042d4-bf21-4d10-81f9-303d3816c114",
"c7820a4f-55c9-44eb-bf7e-b965a9d0e924",
"5b5b6b8c-9b88-4181-9ebb-14abb7783e1e",
"db12030d-7577-435f-9d48-5c1aae615c4f",
"e37c82c5-483f-4e68-8bf3-2a22dcef0b1e",
"0b4cacb1-970f-4ef0-85da-371d81f899e0",
"3949f121-af2b-4c7c-8a49-07befc08abbe",
"73c1ac5a-80a0-4e55-b143-52df5697c9ea",
"0f325614-48c9-4a6a-b7f2-731c26ddeb97",
"572117cf-a76b-44d2-804a-4901bfc545f4",
"33fd4f1f-fb01-4d3a-9b8d-b8d8024a2dab"
],
"test_session_set": [
"66cb6d16-f972-40e9-88c6-64b6816b900c",
"e62b75a0-f1f6-4e45-b3db-17fd7ff5756c",
"13ffc0e4-eebb-4baa-97af-5983ce46779c",
"d062849b-c60f-4369-a113-50f6ce8521fb",
"bb7f1e75-65ee-4d98-adc8-c70551a35940",
"7a6576d8-007c-4ee9-b7cc-8cc2d020e1bf",
"4ce65f3a-21ee-4f77-85c0-b3ddfe1e6381",
"783d7043-b27e-4744-a969-5f40fb40827c",
"81b74694-a2e6-4be1-b9f6-69f3aa03f50d",
"e6317c78-06f0-4c6d-9886-ca67712772ed",
"44bcb1fa-9e64-4bae-927a-4d5d53e536a6",
"218e79aa-3f56-4bd4-aa2a-14027bf7d52d",
"561fd770-f9a2-41a4-90da-beeb7d96ee7f",
"a2d94b61-57c7-4ec2-ad94-94b3585a6b64",
"764a08dc-5ac5-4c09-a281-0210cdd5a55e",
"c1057c9b-331f-458b-8914-2703511e8b13",
"3edc700d-7d14-4154-a3e7-5b0e8c3fde07",
"1492c03c-1887-4576-8889-5bc2848967de",
"2ea37ccc-e1ec-4e0d-a2a7-9707ea300b7f",
"65cad708-34b8-4158-be96-b88710288473",
"be8f4445-3958-4fc4-8cdf-b14d1d0eba73",
"3c62a0d2-10d7-4a9e-ac2c-02505663afad",
"be25331c-b1a0-4187-93fb-3b59437258c3",
"108cebaa-777d-42dd-8b1e-69ea7f443a0a",
"be3b65c8-ba74-4321-99b4-ca0e99e6fc2c",
"3bd74a94-ec3e-4138-b78f-ad76af4f07e2",
"4356f6d5-576a-4999-af40-744c242fff22",
"b0c57748-65c8-49d7-9681-ddcfcde3298b",
"12706b2d-0a0b-4511-8feb-ed8065746e50",
"f0e9a139-86f6-41bc-82f1-11283954a781",
"bb3c6009-892c-4071-9bb4-692b83ed12f3",
"90bf3f44-60ae-4430-9dc6-2365f7901fc5",
"2b5cea2f-b019-4ecf-abbc-b2346bd65a10",
"8e9f89d2-f02a-4c75-8831-6785ef6bd7f5",
"13c24739-e514-4fad-8288-6686bffa71ad",
"1828b207-9601-4521-8c0b-5d9e829fca78",
"0324c3b5-5154-4de1-9869-eb866b568e0f",
"1a746147-2dec-4a37-a51b-c94e4f1de010",
"3a20bf0a-6eb1-47c5-9bf8-8445446feb61",
"c2a8f168-06b0-4766-8865-27f08cb1bcae",
"f55f2dfd-704f-42cf-a39e-72e2560314b3",
"4ea88cb9-bff8-49e0-a855-cc1902db9a99",
"4e0053f8-566c-4952-abbe-4ea877782a0a",
"80ee109f-b097-4755-8a39-9f0b33adb170",
"933ddf59-52d2-4d4c-b653-05de44ded245",
"b432e10e-cff1-4f15-a8fe-1e2350e94b36",
"157c8e81-117a-4a15-a047-9a574181d44b",
"cfc6da55-43fc-4cd8-96d0-9de5166f4a63",
"aabeb7f9-5ada-425a-9545-c98dc443887a",
"db9978fb-fe4d-467b-9495-ab7daf3b1b55",
"6549c35b-8b1d-4577-9434-ac808e4d7057",
"e50126ee-f4c1-41a6-a2e8-79636b40b341",
"49a7e53b-aba5-4e53-b3bc-1492657fc8ae",
"7abe6d4a-f594-4a15-af08-ddd591ff500c",
"03c50078-a08d-43ad-a3c9-87916aebd865",
"a9a0ed92-fab1-4b6e-ae27-714692464641",
"3c3207e6-57b9-4be1-96fc-5b2168ae3cd3",
"895545f1-f194-4c0a-8513-87d386670f15",
"7b76be35-4e6d-42d7-9fa9-14a21fc327cf",
"e4670e4f-0b1a-450a-9778-9ee2409db554",
"34bc808a-4b98-42c5-a1ea-6f565dd4aa20",
"bec4e063-9be1-402e-a7b5-dba20730c933",
"20a3e8e5-3f43-46a5-84fa-6888bd9c5c88",
"5da4efa3-8e54-46dc-a8c2-3016fa4e12f8",
"0d998741-de68-4006-898e-7108de4167b1",
"4d144ff3-a853-4b79-9825-d7fcea248373",
"dce1d8b4-2c29-4fec-a343-77885899c434",
"dab038d7-9e08-4edf-8d03-ee7e2af5a10e",
"a1055434-9e9b-4d69-bac3-374a39f801da",
"e7086057-b5f7-4286-87c6-ab110072c5b0",
"3af65596-1aa5-4126-933b-b98ccaccedf2",
"19f880ae-9770-45a5-93f5-00047f22d5f6",
"36888bbd-1ece-4a3a-8aff-b0f6ef905752",
"f42a7e14-c3ee-4b05-a59b-8d349f8bd19b",
"d36d9514-a99b-4534-b54f-3fd98a19f0ea",
"6c4f7cb0-caa0-4a35-9071-a84c9ec2ab2f",
"666d9243-f4f9-47f5-8fba-cee8568abf41",
"2f14e742-dc58-43c8-8ebc-7244487dbba7",
"f8accf9d-4427-4458-88e9-89d51a3f5136",
"50810487-0b0f-45e0-8f37-515644189e81",
"f0445727-2729-4a76-9434-fb081a14cd9d",
"f55f890d-fd90-43f6-a11a-a1920c21890a",
"16851d4d-35e0-49b4-b3b5-28e67b564db3",
"5c8f8839-4bf9-45e0-beb0-6c17a5ead0d5",
"514d71c8-fd4e-4908-89a4-c0861e81ca8a",
"35465081-a518-4321-8384-477ef5b29b8d",
"de82a6f3-1a55-4996-ae68-35032678ff66"
]
}
}

View File

@@ -0,0 +1,24 @@
#!/bin/bash
PATH_TO_CONFIG_FILE=configs/EasyCom/LSTR/lstr_aa_easycom.yaml
PATH_TO_CHECKPOINT_FILE=checkpoints/lstr_easycom_AV.pth
echo "---"
echo "CUDA_VISIBLE_DEVICES: $CUDA_VISIBLE_DEVICES"
echo "Config file: $PATH_TO_CONFIG_FILE"
# echo "Checkpoint opt: $CHECKPOINT_OPT"
# echo "Epoch opt: $EPOCH_OPT"
echo python tools/test_net.py \
--config_file $PATH_TO_CONFIG_FILE \
--gpu $CUDA_VISIBLE_DEVICES \
MODEL.CHECKPOINT $PATH_TO_CHECKPOINT_FILE \
MODEL.LSTR.INFERENCE_MODE batch \
$@
python tools/test_net.py \
--config_file $PATH_TO_CONFIG_FILE \
--gpu $CUDA_VISIBLE_DEVICES \
MODEL.CHECKPOINT $PATH_TO_CHECKPOINT_FILE \
MODEL.LSTR.INFERENCE_MODE batch \
$@

View File

@@ -0,0 +1,25 @@
#!/bin/bash
PATH_TO_CONFIG_FILE=configs/EasyCom/LSTR/lstr_aa_easycom.yaml
shift # Remove the first argument so $@ contains all other arguments
echo "Config file: $PATH_TO_CONFIG_FILE"
echo "Additional options: $@"
# EXAMPLE:
# USE_WANDB True
# INPUT.MODALITY "visual+motion+object"
echo "---"
echo "CUDA_VISIBLE_DEVICES: $CUDA_VISIBLE_DEVICES"
echo "Config file: $PATH_TO_CONFIG_FILE"
echo python tools/train_net.py \
--config_file $PATH_TO_CONFIG_FILE \
--gpu $CUDA_VISIBLE_DEVICES \
$@
python tools/train_net.py \
--config_file $PATH_TO_CONFIG_FILE \
--gpu $CUDA_VISIBLE_DEVICES \
$@

8
third_party/EgoSpeak/LSTR/setup.py vendored Normal file
View File

@@ -0,0 +1,8 @@
from setuptools import find_packages, setup
setup(
name='LSTR',
version='0.1',
packages=find_packages('src') + ['configs'],
package_dir={'': 'src', 'configs': 'configs'},
)

View File

@@ -0,0 +1,2 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0

View File

@@ -0,0 +1,2 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0

View File

@@ -0,0 +1,4 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
from .defaults import get_cfg

View File

@@ -0,0 +1,171 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
from yacs.config import CfgNode as CN
# ---------------------------------------------------------------------------- #
# Config Definition
# ---------------------------------------------------------------------------- #
_C = CN()
# ---------------------------------------------------------------------------- #
# Metadata
# ---------------------------------------------------------------------------- #
_C.SEED = 0
# ---------------------------------------------------------------------------- #
# Model
# ---------------------------------------------------------------------------- #
_C.MODEL = CN()
_C.MODEL.MODEL_NAME = ''
_C.MODEL.CHECKPOINT = ''
_C.MODEL.EXCEPT_CLASSIFIER = False
# ---------------------------------------------------------------------------- #
# Feature Head
# ---------------------------------------------------------------------------- #
_C.MODEL.FEATURE_HEAD = CN()
_C.MODEL.FEATURE_HEAD.LINEAR_ENABLED = True
_C.MODEL.FEATURE_HEAD.LINEAR_OUT_FEATURES = 1024
_C.MODEL.FEATURE_HEAD.DROPOUT = 0.
# ---------------------------------------------------------------------------- #
# Transformer Network
# ---------------------------------------------------------------------------- #
_C.MODEL.LSTR = CN()
# Hyperparameters
_C.MODEL.LSTR.NUM_HEADS = 8
_C.MODEL.LSTR.DIM_FEEDFORWARD = 1024
_C.MODEL.LSTR.DROPOUT = 0.2
_C.MODEL.LSTR.DROPOUT_CLS = 0.0
_C.MODEL.LSTR.ACTIVATION = 'relu'
# Memory choices
_C.MODEL.LSTR.AGES_MEMORY_SECONDS = 0
_C.MODEL.LSTR.AGES_MEMORY_SAMPLE_RATE = 1
_C.MODEL.LSTR.LONG_MEMORY_SECONDS = 0
_C.MODEL.LSTR.LONG_MEMORY_SAMPLE_RATE = 1
_C.MODEL.LSTR.WORK_MEMORY_SECONDS = 8
_C.MODEL.LSTR.WORK_MEMORY_SAMPLE_RATE = 1
_C.MODEL.LSTR.WORK_MEMORY_USE_PE = True
_C.MODEL.LSTR.LONG_MEMORY_USE_PE = False
_C.MODEL.LSTR.LONG_MEMORY_INCLUDE_WORK = False
_C.MODEL.LSTR.LONG_MEMORY_INCLUDE_WORK2 = False
# Anticipation choices
_C.MODEL.LSTR.ANTICIPATION_SECONDS = 0
_C.MODEL.LSTR.ANTICIPATION_SAMPLE_RATE = 1
# for EK55/100
_C.MODEL.LSTR.V_N_CLASSIFIER = False
_C.MODEL.LSTR.LOSS_ANTICIPATE_ONLY = False
_C.MODEL.LSTR.LOSS_WEIGHT_V = 1.
_C.MODEL.LSTR.LOSS_WEIGHT_N = 1.
_C.MODEL.LSTR.FUSION_WEIGHT_V = 1.
_C.MODEL.LSTR.FUSION_WEIGHT_N = 1.
# Design choices
_C.MODEL.LSTR.ENC_ATTENTION_TYPE = 'dotproduct'
_C.MODEL.LSTR.ENC_ATTENTION_DECAY = 1.0
_C.MODEL.LSTR.ENC_MODULE = [
[16, 1, True], [32, 2, True]
]
_C.MODEL.LSTR.DEC_MODULE = [-1, 2, True]
_C.MODEL.LSTR.FC_NORM = False
# Inference modes
_C.MODEL.LSTR.INFERENCE_MODE = 'batch'
# Position Embedding
# _C.MODEL.LSTR.POSITION_EMBEDDING = 'default'
# ---------------------------------------------------------------------------- #
# Criterion
# ---------------------------------------------------------------------------- #
_C.MODEL.CRITERIONS = [['MCE', {}]]
# ---------------------------------------------------------------------------- #
# Data
# ---------------------------------------------------------------------------- #
_C.DATA = CN()
_C.DATA.DATA_INFO = 'data/data_info.json'
_C.DATA.DATA_NAME = None
_C.DATA.DATA_ROOT = None
_C.DATA.CLASS_NAMES = None
_C.DATA.NUM_CLASSES = None
_C.DATA.IGNORE_INDEX = None
_C.DATA.METRICS = None
_C.DATA.FPS = None
_C.DATA.TRAIN_SESSION_SET = None
_C.DATA.TEST_SESSION_SET = None
_C.DATA.EK_EXT_PATH = 'external/rulstm/RULSTM/data/ek55/'
_C.DATA.CLIP_MIXUP_RATE = 0.
_C.DATA.CLIP_MIXUP_SAMPLE = 'uniform'
# ---------------------------------------------------------------------------- #
# Input
# ---------------------------------------------------------------------------- #
_C.INPUT = CN()
_C.INPUT.MODALITY = 'twostream'
# 'twostream'='visual+motion', 'threestream'='visual+motion+object'
_C.INPUT.VISUAL_FEATURE = 'rgb_anet_resnet50'
_C.INPUT.MOTION_FEATURE = 'flow_anet_resnet50'
_C.INPUT.OBJECT_FEATURE = 'flow_anet_resnet50'
_C.INPUT.TARGET_PERFRAME = 'target_perframe'
# ---------------------------------------------------------------------------- #
# Data Loader
# ---------------------------------------------------------------------------- #
_C.DATA_LOADER = CN()
_C.DATA_LOADER.BATCH_SIZE = 32
_C.DATA_LOADER.NUM_WORKERS = 4
_C.DATA_LOADER.PIN_MEMORY = False
# _C.MODEL.MINIROAD.NUM_WORKERS = 4
# ---------------------------------------------------------------------------- #
# Solver
# ---------------------------------------------------------------------------- #
_C.SOLVER = CN()
_C.SOLVER.START_EPOCH = 1
_C.SOLVER.NUM_EPOCHS = 50
# ---------------------------------------------------------------------------- #
# Optimizer
# ---------------------------------------------------------------------------- #
_C.SOLVER.OPTIMIZER = 'adam'
_C.SOLVER.BASE_LR = 0.00005
_C.SOLVER.WEIGHT_DECAY = 0.00005
_C.SOLVER.MOMENTUM = 0.9
# _C.MODEL.MINIROAD.LR = 0.0001
# _C.MODEL.MINIROAD.WEIGHT_DECAY = 0.05
# ---------------------------------------------------------------------------- #
# Scheduler
# ---------------------------------------------------------------------------- #
_C.SOLVER.SCHEDULER = CN()
_C.SOLVER.SCHEDULER.SCHEDULER_NAME = None
_C.SOLVER.SCHEDULER.MILESTONES = []
_C.SOLVER.SCHEDULER.GAMMA = 0.1
_C.SOLVER.SCHEDULER.WARMUP_FACTOR = 0.3
_C.SOLVER.SCHEDULER.WARMUP_EPOCHS = 10.0
_C.SOLVER.SCHEDULER.WARMUP_METHOD = 'linear'
# ---------------------------------------------------------------------------- #
# Others
# ---------------------------------------------------------------------------- #
_C.SOLVER.PHASES = ['train', 'test']
_C.SOLVER.SAVE_EVERY_EPOCHS = 10
# ---------------------------------------------------------------------------- #
# Output
# ---------------------------------------------------------------------------- #
_C.OUTPUT_DIR = 'checkpoints'
_C.SESSION = ''
_C.USE_WANDB = False
_C.SAVE_HPARAM = False
# ---------------------------------------------------------------------------- #
# Misc
# ---------------------------------------------------------------------------- #
_C.VERBOSE = False
def get_cfg():
return _C.clone()

View File

@@ -0,0 +1,20 @@
# dimension of features extracted from different models
# used in feature_head, dataset, models
FEATURE_SIZES = {
'rgb_anet_resnet50': 2048,
'flow_anet_resnet50': 2048,
'rgb_kinetics_bninception': 1024,
'flow_kinetics_bninception': 1024,
'rgb_farl': 512,
'rgb_kinetics_resnet50': 2048,
'rgb_kinetics_resnet50_debug': 2048,
'rgb_kinetics_resnet50_debug_resized': 2048,
'flow_kinetics_resnet50': 2048,
'flow_nv_kinetics_bninception': 1024,
'rgb_kinetics_i3d': 2048,
'flow_kinetics_i3d': 2048,
'audio_wav2vec2': 5120,
'audio_wav2vec2_left': 5120,
'audio_wav2vec2_right': 5120,
}

View File

@@ -0,0 +1,4 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
from .criterions import build_criterion

View File

@@ -0,0 +1,356 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
__all__ = ['build_criterion']
import os.path as osp
from functools import partial
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import torch.nn.functional as F
from rekognition_online_action_detection.utils.registry import Registry
CRITERIONS = Registry()
@CRITERIONS.register('BCE')
class BinaryCrossEntropyLoss(nn.Module):
def __init__(self, reduction='mean', ignore_index=-100):
super(BinaryCrossEntropyLoss, self).__init__()
self.criterion = nn.BCEWithLogitsLoss(reduction=reduction)
def forward(self, input, target):
return self.criterion(input, target)
@CRITERIONS.register('SCE')
class SingleCrossEntropyLoss(nn.Module):
def __init__(self, reduction='mean', ignore_index=-100):
super(SingleCrossEntropyLoss, self).__init__()
self.criterion = nn.CrossEntropyLoss(
reduction=reduction, ignore_index=ignore_index)
def forward(self, input, target):
return self.criterion(input, target)
@CRITERIONS.register('MCE')
class MultipCrossEntropyLoss(nn.Module):
def __init__(self, reduction='mean', ignore_index=-100):
super(MultipCrossEntropyLoss, self).__init__()
self.reduction = reduction
self.ignore_index = ignore_index
def forward(self, input, target):
logsoftmax = nn.LogSoftmax(dim=-1).to(input.device)
# LSTR transform input from [16,50,3] to [800,3]
output = torch.sum(-target * logsoftmax(input), dim=1)
if self.reduction == 'mean':
return torch.mean(output)
elif self.reduction == 'sum':
return torch.sum(output)
else:
return output
# if self.ignore_index >= 0:
# notice_index = [i for i in range(target.shape[-1]) if i != self.ignore_index]
# output = torch.sum(-target[:, notice_index] * logsoftmax(input[:, notice_index]), dim=1)
# if (target[:, self.ignore_index] != 1).sum().item() == 0:
# return torch.mean(torch.zeros_like(output))
# if self.reduction == 'mean':
# return torch.mean(output[target[:, self.ignore_index] != 1])
# elif self.reduction == 'sum':
# return torch.sum(output[target[:, self.ignore_index] != 1])
# else:
# return output[target[:, self.ignore_index] != 1]
# else:
# output = torch.sum(-target * logsoftmax(input), dim=1)
# if self.reduction == 'mean':
# return torch.mean(output)
# elif self.reduction == 'sum':
# return torch.sum(output)
# else:
# return output
@CRITERIONS.register('MCE_MiniROAD')
class MiniROADMultipCrossEntropyLoss(nn.Module):
def __init__(self, reduction='mean', ignore_index=-100):
super(MiniROADMultipCrossEntropyLoss, self).__init__()
self.reduction = reduction
self.ignore_index = ignore_index
def forward(self, input, target):
logsoftmax = nn.LogSoftmax(dim=-1).to(input.device)
output = torch.sum(-F.normalize(target) * logsoftmax(input), dim=1) # B
if self.reduction == 'mean':
loss = torch.mean(output)
elif self.reduction == 'sum':
loss = torch.sum(output)
return loss
@CRITERIONS.register('MCE_ANT')
class AntMultipCrossEntropyLoss(nn.Module):
def __init__(self, reduction='mean', ignore_index=-100):
super(AntMultipCrossEntropyLoss, self).__init__()
self.reduction = reduction
self.ignore_index = ignore_index
# only ant_target loss
def forward(self, input, target):
logsoftmax = nn.LogSoftmax(dim=-1).to(input.device)
import pdb; pdb.set_trace()
if self.ignore_index >= 0:
notice_index = [i for i in range(target.shape[-1]) if i != self.ignore_index]
import pdb; pdb.set_trace()
# input dim: [16, 128, 10, 3]
# target.shape: [16, 10, 3]
output = torch.sum()
output = torch.sum(-target[:, notice_index] * logsoftmax(input[:, notice_index]), dim=1)
if (target[:, self.ignore_index] != 1).sum().item() == 0:
return torch.mean(torch.zeros_like(output))
if self.reduction == 'mean':
return torch.mean(output[target[:, self.ignore_index] != 1])
elif self.reduction == 'sum':
return torch.sum(output[target[:, self.ignore_index] != 1])
else:
return output[target[:, self.ignore_index] != 1]
else:
output = torch.sum(-target * logsoftmax(input), dim=1)
if self.reduction == 'mean':
return torch.mean(output)
elif self.reduction == 'sum':
return torch.sum(output)
else:
return output
@CRITERIONS.register('MCE_EQL')
class MultipCrossEntropyEqualizedLoss(nn.Module):
def __init__(self, gamma=0.95, lambda_=3e-3, reduction='mean', ignore_index=-100,
anno_path='external/rulstm/RULSTM/data/ek55/'):
super(MultipCrossEntropyEqualizedLoss, self).__init__()
# get label distribution
segment_list = pd.read_csv(osp.join(anno_path, 'training.csv'),
names=['id', 'video', 'start_f', 'end_f', 'verb', 'noun', 'action'],
skipinitialspace=True)
freq_info = np.zeros((max(segment_list['action']) + 1,))
assert ignore_index == 0
for segment in segment_list.iterrows():
freq_info[segment[1]['action']] += 1.
freq_info = freq_info / freq_info.sum()
self.freq_info = torch.FloatTensor(freq_info)
self.gamma = gamma
self.lambda_ = lambda_
self.reduction = reduction
self.ignore_index = ignore_index
def forward(self, input, target):
logsoftmax = nn.LogSoftmax(dim=1).to(input.device)
bg_target = target[:, self.ignore_index]
notice_index = [i for i in range(target.shape[-1]) if i != self.ignore_index]
input = input[:, notice_index]
target = target[:, notice_index]
weight = input.new_zeros(len(notice_index))
weight[self.freq_info < self.lambda_] = 1.
weight = weight.view(1, -1).repeat(input.shape[0], 1)
eql_w = 1 - (torch.rand_like(target) < self.gamma) * weight * (1 - target)
input = torch.log(eql_w + 1e-8) + input
output = torch.sum(-target * logsoftmax(input), dim=1)
if (bg_target != 1).sum().item() == 0:
return torch.mean(torch.zeros_like(output))
if self.reduction == 'mean':
return torch.mean(output[bg_target != 1])
elif self.reduction == 'sum':
return torch.sum(output[bg_target != 1])
else:
return output[bg_target != 1]
@CRITERIONS.register('EQLv2')
class EQLv2Loss(nn.Module):
def __init__(self, gamma=12, mu=0.8, alpha=4.0, reduction='mean', ignore_index=-100,
num_classes=3806):
super(EQLv2Loss, self).__init__()
self.num_classes = num_classes
self.gamma = gamma
self.mu = mu
self.alpha = alpha
self.ignore_index = ignore_index
self._pos_grad = None
self._neg_grad = None
self.pos_neg = None
def _func(x, gamma, mu):
return 1 / (1 + torch.exp(-gamma * (x - mu)))
self.map_func = partial(_func, gamma=self.gamma, mu=self.mu)
def forward(self, input, target):
self.n_i, self.n_c = input.size()
pos_w, neg_w = self.get_weight(input)
weight = pos_w * target + neg_w * (1 - target)
cls_loss = F.binary_cross_entropy_with_logits(input, target, reduction='none')
cls_loss = torch.sum(cls_loss * weight) / self.n_i
self.collect_grad(input.detach(), target.detach(), weight.detach())
return cls_loss
def collect_grad(self, cls_score, target, weight):
prob = torch.sigmoid(cls_score)
grad = target * (prob - 1) + (1 - target) * prob
grad = torch.abs(grad)
# do not collect grad for objectiveness branch [:-1]
pos_grad = torch.sum(grad * target * weight, dim=0)[1:]
neg_grad = torch.sum(grad * (1 - target) * weight, dim=0)[1:]
# dist.all_reduce(pos_grad)
# dist.all_reduce(neg_grad)
self._pos_grad += pos_grad
self._neg_grad += neg_grad
self.pos_neg = self._pos_grad / (self._neg_grad + 1e-10)
def get_weight(self, cls_score):
if self._pos_grad is None:
self._pos_grad = cls_score.new_zeros(self.num_classes)
self._neg_grad = cls_score.new_zeros(self.num_classes)
neg_w = cls_score.new_zeros((self.n_i, self.n_c))
pos_w = cls_score.new_zeros((self.n_i, self.n_c))
else:
neg_w = torch.cat([cls_score.new_ones(1), self.map_func(self.pos_neg)])
pos_w = 1 + self.alpha * (1 - neg_w)
neg_w = neg_w.view(1, -1).expand(self.n_i, self.n_c)
pos_w = pos_w.view(1, -1).expand(self.n_i, self.n_c)
return pos_w, neg_w
@CRITERIONS.register('MCE_SEESAW')
class MultipCrossEntropySeesawLoss(nn.Module):
def __init__(self, reduction='mean', ignore_index=-100,
p=0.8, q=2.0, eps=1e-2,
anno_path='external/rulstm/RULSTM/data/ek55/'):
super(MultipCrossEntropySeesawLoss, self).__init__()
# get label distribution
segment_list = pd.read_csv(osp.join(anno_path, 'training.csv'),
names=['id', 'video', 'start_f', 'end_f', 'verb', 'noun', 'action'],
skipinitialspace=True)
self.num_classes = max(segment_list['action']) + 1
self.cum_samples = torch.zeros(self.num_classes, dtype=torch.float)
self.p = p
self.q = q
self.eps = eps
self.reduction = reduction
assert ignore_index == 0
self.ignore_index = ignore_index
def forward(self, input, target):
logsoftmax = nn.LogSoftmax(dim=1).to(input.device)
self.cum_samples = self.cum_samples.to(input.device)
bg_target = target[:, self.ignore_index]
notice_index = [i for i in range(target.shape[-1]) if i != self.ignore_index]
input = input[:, notice_index]
target = target[:, notice_index]
# accumulate the samples for each category
self.cum_samples += target.sum(0)
seesaw_weights = input.new_ones(target.size())
if self.p > 0:
sample_ratio_matrix = self.cum_samples[None, :].clamp(min=1) / self.cum_samples[:, None].clamp(min=1)
index = (sample_ratio_matrix < 1.0).float()
sample_weights = sample_ratio_matrix.pow(self.p) * index + (1 - index)
for ni in range(input.shape[0]):
true_target = target[ni, :].nonzero(as_tuple=True)[0]
## TODO: study mean/min/max/sum
if len(true_target) > 0:
mitigation_factor = torch.min(sample_weights[true_target, :], dim=0).values
seesaw_weights[ni, :] = seesaw_weights[ni, :] * mitigation_factor
if self.q > 0:
scores = F.softmax(input.detach(), dim=1)
self_scores = input.new_ones([input.shape[0]])
for ni in range(input.shape[0]):
true_target = target[ni, :].nonzero(as_tuple=True)[0]
## TODO: study mean/min/max/sum
if len(true_target) > 0:
self_scores[ni] = torch.min(scores[ni, true_target])
score_matrix = scores / self_scores[:, None].clamp(min=self.eps)
index = (score_matrix > 1.0).float()
compensation_factor = score_matrix.pow(self.q) * index + (1 - index)
seesaw_weights = seesaw_weights * compensation_factor
input = input + (seesaw_weights.log() * (1 - target))
output = torch.sum(-target * logsoftmax(input), dim=1)
if (bg_target != 1).sum().item() == 0:
return torch.mean(torch.zeros_like(output))
if self.reduction == 'mean':
return torch.mean(output[bg_target != 1])
elif self.reduction == 'sum':
return torch.sum(output[bg_target != 1])
else:
return output[bg_target != 1]
@CRITERIONS.register('PRED_FUTURE')
class PredictFutureLoss(nn.Module):
def __init__(self, reduction='mean', ignore_index=-100):
super(PredictFutureLoss, self).__init__()
self.reduction = reduction
self.criterion = nn.MSELoss(reduction=reduction)
def forward(self, pred, true):
return self.criterion(pred, true)
def build_criterion(cfg, device=None):
criterion = {}
for name, params in cfg.MODEL.CRITERIONS:
if name in CRITERIONS:
if 'ignore_index' not in params:
params['ignore_index'] = cfg.DATA.IGNORE_INDEX
criterion[name] = CRITERIONS[name](**params).to(device)
else:
raise RuntimeError('Unknown criterion: {}'.format(name))
return criterion

View File

@@ -0,0 +1,5 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
from .datasets import build_data_loader, build_dataset
from .perframe_data_layers import *

View File

@@ -0,0 +1,31 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
__all__ = [
'build_dataset',
'build_data_loader',
]
import torch.utils.data as data
from rekognition_online_action_detection.utils.registry import Registry
DATA_LAYERS = Registry()
def build_dataset(cfg, phase, tag=''):
data_names = ['YTConv', 'EGO4D']
data_name = next((name for name in data_names if name in cfg.DATA.DATA_NAME), cfg.DATA.DATA_NAME)
data_layer = DATA_LAYERS[cfg.MODEL.MODEL_NAME + tag + data_name]
return data_layer(cfg, phase)
def build_data_loader(cfg, phase):
data_loader = data.DataLoader(
dataset=build_dataset(cfg, phase),
batch_size=cfg.DATA_LOADER.BATCH_SIZE,
shuffle=True if phase == 'train' else False,
num_workers=cfg.DATA_LOADER.NUM_WORKERS,
pin_memory=cfg.DATA_LOADER.PIN_MEMORY,
)
return data_loader

View File

@@ -0,0 +1,453 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import os.path as osp
import warnings
from bisect import bisect_right
import numpy as np
import pandas as pd
import torch
import torch.utils.data as data
from rekognition_online_action_detection.utils.ek_utils import (
action_to_noun_map, action_to_verb_map)
from .datasets import DATA_LAYERS as registry
@registry.register('LSTRTHUMOS')
@registry.register('LSTRTVSeries')
@registry.register('LSTREK55')
@registry.register('LSTREK100')
@registry.register('LSTRYTConv')
@registry.register('LSTREGO4D')
@registry.register('LSTREasyCom')
class LSTRDataLayer(data.Dataset):
def __init__(self, cfg, phase='train'):
self.cfg = cfg
self.data_name = cfg.DATA.DATA_NAME
self.data_root = cfg.DATA.DATA_ROOT
self.clip_mixup_rate = cfg.DATA.CLIP_MIXUP_RATE
self.clip_mixup_sample = cfg.DATA.CLIP_MIXUP_SAMPLE
self.visual_feature = cfg.INPUT.VISUAL_FEATURE
self.motion_feature = cfg.INPUT.MOTION_FEATURE
self.object_feature = cfg.INPUT.OBJECT_FEATURE
self.target_perframe = cfg.INPUT.TARGET_PERFRAME
self.sessions = getattr(cfg.DATA, phase.upper() + '_SESSION_SET')
self.long_memory_length = cfg.MODEL.LSTR.LONG_MEMORY_LENGTH
self.long_memory_sample_rate = cfg.MODEL.LSTR.LONG_MEMORY_SAMPLE_RATE
self.long_memory_num_samples = cfg.MODEL.LSTR.LONG_MEMORY_NUM_SAMPLES
self.work_memory_length = cfg.MODEL.LSTR.WORK_MEMORY_LENGTH
self.work_memory_sample_rate = cfg.MODEL.LSTR.WORK_MEMORY_SAMPLE_RATE
self.work_memory_num_samples = cfg.MODEL.LSTR.WORK_MEMORY_NUM_SAMPLES
self.anticipation_length = cfg.MODEL.LSTR.ANTICIPATION_LENGTH
self.anticipation_sample_rate = cfg.MODEL.LSTR.ANTICIPATION_SAMPLE_RATE
self.anticipation_num_samples = cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES
self.training = phase == 'train'
self._init_dataset()
def shuffle(self):
self._init_dataset()
def _init_dataset(self):
self.inputs = []
if self.data_name.startswith('EK'):
if self.data_name == 'EK55':
path_to_data = 'external/rulstm/RULSTM/data/ek55/'
elif self.data_name == 'EK100':
path_to_data = 'external/rulstm/RULSTM/data/ek100/'
else:
raise ValueError
segment_list = pd.read_csv(osp.join(path_to_data,
'training.csv' if self.training else 'validation.csv'),
names=['id', 'video', 'start_f', 'end_f', 'verb', 'noun', 'action'],
skipinitialspace=True)
segment_list['len_f'] = segment_list['end_f'] - segment_list['start_f']
self.segment_by_cls = {cls: segment_list[segment_list['action'] == cls]
for cls in range(0, self.cfg.DATA.NUM_CLASSES - 1)}
for session in self.sessions:
target = np.load(osp.join(self.data_root, self.target_perframe, session + '.npy'))
if self.cfg.MODEL.LSTR.V_N_CLASSIFIER:
verb_target = np.load(osp.join(self.data_root, self.target_perframe.replace('target', 'verb'), session + '.npy'))
noun_target = np.load(osp.join(self.data_root, self.target_perframe.replace('target', 'noun'), session + '.npy'))
else:
verb_target = target
noun_target = target
segments_per_session = segment_list[segment_list['video'] == session]
for segment in segments_per_session.iterrows():
start_tick = int(segment[1]['start_f'] / 30 * self.cfg.DATA.FPS)
end_tick = int(segment[1]['end_f'] / 30 * self.cfg.DATA.FPS)
start_tick += np.random.randint(self.anticipation_length) if self.training else 0
start_tick = min(start_tick, end_tick)
work_end = start_tick - self.anticipation_length
work_start = work_end - self.work_memory_length
segments_before_current = segments_per_session[segments_per_session['end_f'] < segment[1]['start_f']]
if work_start < 0:
continue
self.inputs.append([
session, work_start, work_end,
target[work_start: work_end + self.anticipation_length],
verb_target[work_start: work_end + self.anticipation_length],
noun_target[work_start: work_end + self.anticipation_length],
segments_before_current,
])
else:
for session in self.sessions:
target = np.load(osp.join(self.data_root, self.target_perframe, session + '.npy'))
if self.cfg.MODEL.LSTR.V_N_CLASSIFIER:
verb_target = np.load(osp.join(self.data_root, self.target_perframe.replace('target', 'verb'), session + '.npy'))
noun_target = np.load(osp.join(self.data_root, self.target_perframe.replace('target', 'noun'), session + '.npy'))
else:
verb_target = target
noun_target = target
seed = np.random.randint(self.work_memory_length + self.anticipation_length) if self.training else 0
for work_start, work_end in zip(
range(seed, target.shape[0], self.work_memory_length + self.anticipation_length),
range(seed + self.work_memory_length, target.shape[0] - self.anticipation_length, self.work_memory_length + self.anticipation_length)):
self.inputs.append([
session, work_start, work_end,
target[work_start: work_end + self.anticipation_length],
verb_target[work_start: work_end + self.anticipation_length],
noun_target[work_start: work_end + self.anticipation_length],
None,
])
def segment_sampler(self, start, end, num_samples):
indices = np.linspace(start, end, num_samples)
return np.sort(indices).astype(np.int32)
def uniform_sampler(self, start, end, num_samples, sample_rate):
if start < 0:
start = (end + 1) % sample_rate
indices = np.arange(start, end + 1)[::sample_rate]
padding = num_samples - indices.shape[0]
if padding > 0:
indices = np.concatenate((np.zeros(padding), indices))
return np.sort(indices).astype(np.int32)
def __getitem__(self, index):
(session, work_start, work_end, target,
verb_target, noun_target, segments_before) = self.inputs[index]
visual_inputs = np.load(
osp.join(self.data_root, self.visual_feature, session + '.npy'), mmap_mode='r')
motion_inputs = np.load(
osp.join(self.data_root, self.motion_feature, session + '.npy'), mmap_mode='r')
object_inputs = np.load(
osp.join(self.data_root, self.object_feature, session + '.npy'), mmap_mode='r')
target = np.concatenate((target[:self.work_memory_length:self.work_memory_sample_rate],
target[self.work_memory_length::self.anticipation_sample_rate]),
axis=0)
if self.cfg.MODEL.LSTR.V_N_CLASSIFIER:
verb_target = np.concatenate((verb_target[:self.work_memory_length:self.work_memory_sample_rate],
verb_target[self.work_memory_length::self.anticipation_sample_rate]),
axis=0)
noun_target = np.concatenate((noun_target[:self.work_memory_length:self.work_memory_sample_rate],
noun_target[self.work_memory_length::self.anticipation_sample_rate]),
axis=0)
work_indices = np.arange(work_start, work_end).clip(0)
work_indices = work_indices[::self.work_memory_sample_rate]
work_visual_inputs = visual_inputs[work_indices]
work_motion_inputs = motion_inputs[work_indices]
work_object_inputs = object_inputs[work_indices]
if self.long_memory_num_samples > 0:
long_start, long_end = work_start - self.long_memory_length, work_start - 1
if self.training:
long_indices = self.segment_sampler(
long_start,
long_end,
self.long_memory_num_samples).clip(0)
else:
long_indices = self.uniform_sampler(
long_start,
long_end,
self.long_memory_num_samples,
self.long_memory_sample_rate).clip(0)
long_visual_inputs = visual_inputs[long_indices]
long_motion_inputs = motion_inputs[long_indices]
long_object_inputs = object_inputs[long_indices]
if self.training and self.clip_mixup_rate > 0:
assert segments_before is not None
segments_before = segments_before[segments_before['end_f'] / 30 * self.cfg.DATA.FPS > long_start]
if self.clip_mixup_sample == 'uniform':
prob = None
elif self.clip_mixup_sample == 'by_length':
prob = (segments_before['len_f']).tolist()
prob = prob / np.sum(prob)
else:
raise ValueError
num_clip = int(len(segments_before) * self.clip_mixup_rate)
segments_to_mixup = segments_before.sample(num_clip, replace=False,
weights=prob)
for old_segment in segments_to_mixup.iterrows():
old_start_tick = int(old_segment[1]['start_f'] / 30 * self.cfg.DATA.FPS)
old_end_tick = int(np.ceil(old_segment[1]['end_f'] / 30 * self.cfg.DATA.FPS))
old_action = old_segment[1]['action']
old_vid = old_segment[1]['video']
assert old_vid == session
segment_same_class = self.segment_by_cls[old_action]
segment_same_class = segment_same_class[segment_same_class['video'] != old_vid]
valid_segments = segment_same_class[segment_same_class['len_f'] > old_segment[1]['len_f']]
if len(valid_segments) == 0:
continue
else:
sample_segment = valid_segments.sample(1)
new_vid = sample_segment['video'].values[0]
new_start_tick = int(sample_segment['start_f'] / 30 * self.cfg.DATA.FPS)
new_end_tick = int(sample_segment['end_f'] / 30 * self.cfg.DATA.FPS)
new_visual_inputs = np.load(
osp.join(self.data_root, self.visual_feature, new_vid + '.npy'), mmap_mode='r')
new_motion_inputs = np.load(
osp.join(self.data_root, self.motion_feature, new_vid + '.npy'), mmap_mode='r')
new_object_inputs = np.load(
osp.join(self.data_root, self.object_feature, new_vid + '.npy'), mmap_mode='r')
sel_indices = np.where((long_indices >= old_start_tick) & (long_indices <= old_end_tick))
shift = np.random.randint(new_end_tick - new_start_tick - len(sel_indices) + 1)
long_visual_inputs[sel_indices] = new_visual_inputs[new_start_tick + shift:new_start_tick + shift + len(sel_indices)]
long_motion_inputs[sel_indices] = new_motion_inputs[new_start_tick + shift:new_start_tick + shift + len(sel_indices)]
long_object_inputs[sel_indices] = new_object_inputs[new_start_tick + shift:new_start_tick + shift + len(sel_indices)]
memory_key_padding_mask = np.zeros(long_indices.shape[0])
last_zero = bisect_right(long_indices, 0) - 1
if last_zero > 0:
memory_key_padding_mask[:last_zero] = float('-inf')
else:
long_visual_inputs = None
long_motion_inputs = None
long_object_inputs = None
memory_key_padding_mask = None
if (long_visual_inputs is not None and
long_motion_inputs is not None and
long_object_inputs is not None):
fusion_visual_inputs = np.concatenate((long_visual_inputs, work_visual_inputs))
fusion_motion_inputs = np.concatenate((long_motion_inputs, work_motion_inputs))
fusion_object_inputs = np.concatenate((long_object_inputs, work_object_inputs))
else:
fusion_visual_inputs = work_visual_inputs
fusion_motion_inputs = work_motion_inputs
fusion_object_inputs = work_object_inputs
fusion_visual_inputs = torch.as_tensor(fusion_visual_inputs.astype(np.float32))
fusion_motion_inputs = torch.as_tensor(fusion_motion_inputs.astype(np.float32))
fusion_object_inputs = torch.as_tensor(fusion_object_inputs.astype(np.float32))
target = torch.as_tensor(target.astype(np.float32))
if self.cfg.MODEL.LSTR.V_N_CLASSIFIER:
verb_target = torch.as_tensor(verb_target.astype(np.float32))
noun_target = torch.as_tensor(noun_target.astype(np.float32))
target = (target, verb_target, noun_target)
assert fusion_visual_inputs.ndim == fusion_motion_inputs.ndim
if fusion_visual_inputs.ndim == 4:
fusion_visual_inputs = fusion_visual_inputs.permute(0, 2, 3, 1)
fusion_motion_inputs = fusion_motion_inputs.permute(0, 2, 3, 1)
fusion_visual_inputs = fusion_visual_inputs.mean((1, 2))
fusion_motion_inputs = fusion_motion_inputs.mean((1, 2))
if memory_key_padding_mask is not None:
memory_key_padding_mask = torch.as_tensor(memory_key_padding_mask.astype(np.float32))
return (fusion_visual_inputs, fusion_motion_inputs,
fusion_object_inputs, memory_key_padding_mask, target)
else:
return (fusion_visual_inputs, fusion_motion_inputs,
fusion_object_inputs, fusion_object_inputs, target)
def __len__(self):
return len(self.inputs)
@registry.register('LSTRBatchInferenceTHUMOS')
@registry.register('LSTRBatchInferenceEGO4D')
@registry.register('LSTRBatchInferenceEasyCom')
@registry.register('LSTRBatchInferenceTVSeries')
@registry.register('LSTRBatchInferenceEK55')
@registry.register('LSTRBatchInferenceEK100')
class LSTRBatchInferenceDataLayer(data.Dataset):
def __init__(self, cfg, phase='test'):
self.cfg = cfg
self.data_root = cfg.DATA.DATA_ROOT
self.visual_feature = cfg.INPUT.VISUAL_FEATURE
self.motion_feature = cfg.INPUT.MOTION_FEATURE
self.object_feature = cfg.INPUT.OBJECT_FEATURE
self.target_perframe = cfg.INPUT.TARGET_PERFRAME
self.sessions = getattr(cfg.DATA, phase.upper() + '_SESSION_SET')
self.long_memory_length = cfg.MODEL.LSTR.LONG_MEMORY_LENGTH
self.long_memory_sample_rate = cfg.MODEL.LSTR.LONG_MEMORY_SAMPLE_RATE
self.long_memory_num_samples = cfg.MODEL.LSTR.LONG_MEMORY_NUM_SAMPLES
self.work_memory_length = cfg.MODEL.LSTR.WORK_MEMORY_LENGTH
self.work_memory_sample_rate = cfg.MODEL.LSTR.WORK_MEMORY_SAMPLE_RATE
self.work_memory_num_samples = cfg.MODEL.LSTR.WORK_MEMORY_NUM_SAMPLES
self.anticipation_length = cfg.MODEL.LSTR.ANTICIPATION_LENGTH
self.anticipation_sample_rate = cfg.MODEL.LSTR.ANTICIPATION_SAMPLE_RATE
self.anticipation_num_samples = cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES
if phase != 'test':
warnings.warn('originally, phase must be `test` for batch inference, got {}'.format(phase))
self.inputs = []
for session in self.sessions:
target = np.load(osp.join(self.data_root, self.target_perframe, session + '.npy'))
if self.cfg.MODEL.LSTR.V_N_CLASSIFIER:
verb_target = np.load(osp.join(self.data_root, self.target_perframe.replace('target', 'verb'), session + '.npy'))
noun_target = np.load(osp.join(self.data_root, self.target_perframe.replace('target', 'noun'), session + '.npy'))
else:
verb_target = target
noun_target = target
for work_start, work_end in zip(
range(0, target.shape[0] + 1),
range(self.work_memory_length, target.shape[0] + 1)):
self.inputs.append([
session, work_start, work_end,
target[work_start: work_end],
verb_target[work_start: work_end],
noun_target[work_start: work_end],
target.shape[0]
])
def uniform_sampler(self, start, end, num_samples, sample_rate):
if start < 0:
start = (end + 1) % sample_rate
indices = np.arange(start, end + 1)[::sample_rate]
padding = num_samples - indices.shape[0]
if padding > 0:
indices = np.concatenate((np.zeros(padding), indices))
return np.sort(indices).astype(np.int32)
def __getitem__(self, index):
session, work_start, work_end, target, verb_target, noun_target, num_frames = self.inputs[index]
visual_inputs = np.load(
osp.join(self.data_root, self.visual_feature, session + '.npy'), mmap_mode='r')
motion_inputs = np.load(
osp.join(self.data_root, self.motion_feature, session + '.npy'), mmap_mode='r')
object_inputs = np.load(
osp.join(self.data_root, self.object_feature, session + '.npy'), mmap_mode='r')
target = target[::self.work_memory_sample_rate]
verb_target = verb_target[::self.work_memory_sample_rate]
noun_target = noun_target[::self.work_memory_sample_rate]
work_indices = np.arange(work_start, work_end).clip(0)
work_indices = work_indices[::self.work_memory_sample_rate]
work_visual_inputs = visual_inputs[work_indices]
work_motion_inputs = motion_inputs[work_indices]
work_object_inputs = object_inputs[work_indices]
if self.long_memory_num_samples > 0:
long_start, long_end = work_start - self.long_memory_length, work_start - 1
long_indices = self.uniform_sampler(
long_start,
long_end,
self.long_memory_num_samples,
self.long_memory_sample_rate).clip(0)
long_visual_inputs = visual_inputs[long_indices]
long_motion_inputs = motion_inputs[long_indices]
long_object_inputs = object_inputs[long_indices]
memory_key_padding_mask = np.zeros(long_indices.shape[0])
last_zero = bisect_right(long_indices, 0) - 1
if last_zero > 0:
memory_key_padding_mask[:last_zero] = float('-inf')
else:
long_visual_inputs = None
long_motion_inputs = None
long_object_inputs = None
memory_key_padding_mask = None
if long_visual_inputs is not None and long_motion_inputs is not None:
fusion_visual_inputs = np.concatenate((long_visual_inputs, work_visual_inputs))
fusion_motion_inputs = np.concatenate((long_motion_inputs, work_motion_inputs))
fusion_object_inputs = np.concatenate((long_object_inputs, work_object_inputs))
else:
fusion_visual_inputs = work_visual_inputs
fusion_motion_inputs = work_motion_inputs
fusion_object_inputs = work_object_inputs
fusion_visual_inputs = torch.as_tensor(fusion_visual_inputs.astype(np.float32))
fusion_motion_inputs = torch.as_tensor(fusion_motion_inputs.astype(np.float32))
fusion_object_inputs = torch.as_tensor(fusion_object_inputs.astype(np.float32))
target = torch.as_tensor(target.astype(np.float32))
if self.cfg.MODEL.LSTR.V_N_CLASSIFIER:
verb_target = torch.as_tensor(verb_target.astype(np.float32))
noun_target = torch.as_tensor(noun_target.astype(np.float32))
target = (target, verb_target, noun_target)
if memory_key_padding_mask is not None:
memory_key_padding_mask = torch.as_tensor(memory_key_padding_mask.astype(np.float32))
return (fusion_visual_inputs, fusion_motion_inputs,
fusion_object_inputs, memory_key_padding_mask, target,
session, work_indices, num_frames)
else:
return (fusion_visual_inputs, fusion_motion_inputs,
fusion_object_inputs, target,
session, work_indices, num_frames)
def __len__(self):
return len(self.inputs)

View File

@@ -0,0 +1,6 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
from .engines import do_inference, do_train
from .lstr.lstr_inference import *
from .lstr.lstr_trainer import *

View File

@@ -0,0 +1,2 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0

View File

@@ -0,0 +1,673 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import os
import os.path as osp
import pickle as pkl
import time
import numpy as np
import pandas as pd
import torch
from rekognition_online_action_detection.datasets import build_dataset
from rekognition_online_action_detection.evaluation import compute_result
from tqdm import tqdm
def batch_inference(cfg, model, device, logger, data_loader):
# Collect scores and targets
pred_scores = {}
pred_scores_verb = {}
pred_scores_noun = {}
gt_targets = {}
start_time = time.time()
with torch.no_grad():
pbar = tqdm(data_loader, desc='BatchInference')
for batch_idx, data in enumerate(pbar, start=1):
target = data[-4]
if cfg.MODEL.LSTR.V_N_CLASSIFIER:
target, verb_target, noun_target = target
score = model(*[x.to(device) for x in data[:-4]])
if cfg.MODEL.LSTR.V_N_CLASSIFIER:
score, score_verb, score_noun = score
score = score.cpu().numpy()
score_verb = score_verb.cpu().numpy()
score_noun = score_noun.cpu().numpy()
cfg.DATA.NUM_VERBS = 126 if cfg.DATA.DATA_NAME == 'EK55' else 98
cfg.DATA.NUM_NOUNS = 353 if cfg.DATA.DATA_NAME == 'EK55' else 301
else:
score = (score.cpu().numpy()
if cfg.DATA.DATA_NAME.startswith('EK')
else score.softmax(dim=-1).cpu().numpy())
for bs, (session, query_indices, num_frames) in enumerate(zip(*data[-3:])):
# data[-3] is session, data[-2] is work_indices, data[-1] is num_frames
if session not in pred_scores:
if cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES > 0:
pred_scores[session] = np.zeros((num_frames, cfg.DATA.NUM_CLASSES, cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES))
if cfg.MODEL.LSTR.V_N_CLASSIFIER:
pred_scores_verb[session] = np.zeros((num_frames, cfg.DATA.NUM_VERBS, cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES))
pred_scores_noun[session] = np.zeros((num_frames, cfg.DATA.NUM_NOUNS, cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES))
else:
pred_scores[session] = np.zeros((num_frames, cfg.DATA.NUM_CLASSES))
if session not in gt_targets:
gt_targets[session] = np.zeros((num_frames, cfg.DATA.NUM_CLASSES))
# DEFAULT VALUES in config
# cfg.MODEL.LSTR.WORK_MEMORY_LENGTH: 40
# cfg.MODEL.LSTR.WORK_MEMORY_SAMPLE_RATE: 1
# cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES: 10
# query_indices.shape: torch.Size([cfg.MODEL.LSTR.WORK_MEMORY_LENGTH])
# query_indices at first: [0, 1, 2, 3, ..., 39]
# At first, go into this if statement
if query_indices[0] in torch.arange(0, cfg.MODEL.LSTR.WORK_MEMORY_SAMPLE_RATE):
if cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES > 0:
for t_a in range(0, cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES):
# at first, full_indices indicate the range:
# work_memory + t_a (anticiptaion timestep, from 0 to 9)
full_indices = torch.cat((query_indices,
torch.arange(query_indices[-1] + cfg.MODEL.LSTR.WORK_MEMORY_SAMPLE_RATE,
query_indices[-1] + (t_a + 1) + cfg.MODEL.LSTR.WORK_MEMORY_SAMPLE_RATE)),
dim=0)
# then assgin the currnet logits + anticipated logits to the pred_scores
pred_scores[session][full_indices, :, t_a] = score[bs][:full_indices.shape[0]]
if cfg.MODEL.LSTR.V_N_CLASSIFIER:
pred_scores_verb[session][full_indices, :, t_a] = score_verb[bs][:full_indices.shape[0]]
pred_scores_noun[session][full_indices, :, t_a] = score_noun[bs][:full_indices.shape[0]]
gt_targets[session][query_indices] = target[bs][:query_indices.shape[0]]
else:
# no anticipation
pred_scores[session][query_indices] = score[bs]
gt_targets[session][query_indices] = target[bs]
else:
if cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES > 0:
for t_a in range(0, cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES):
if query_indices[-1] + t_a + 1 < num_frames:
pred_scores[session][query_indices[-1] + t_a + 1, :, t_a] = score[bs][t_a - cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES]
if cfg.MODEL.LSTR.V_N_CLASSIFIER:
pred_scores_verb[session][query_indices[-1] + t_a + 1, :, t_a] = score_verb[bs][t_a - cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES]
pred_scores_noun[session][query_indices[-1] + t_a + 1, :, t_a] = score_noun[bs][t_a - cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES]
gt_targets[session][query_indices[-1]] = target[bs][-1]
else:
pred_scores[session][query_indices[-1]] = score[bs][-1]
gt_targets[session][query_indices[-1]] = target[bs][-1]
time_taken = time.time() - start_time
# Save scores and targets
# pkl.dump({
# 'cfg': cfg,
# 'perframe_pred_scores': pred_scores,
# 'perframe_gt_targets': gt_targets,
# }, open(osp.splitext(cfg.MODEL.CHECKPOINT)[0] + '.pkl', 'wb'))
num_frames = np.concatenate(list(gt_targets.values()), axis=0).shape[0]
logger.info(f'\nProcessed {num_frames} frames in {time_taken:.1f} seconds ({num_frames / time_taken :.1f} FPS)')
logger.info('\n=============')
# Compute results
if cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES > 0:
maps_list = []
per_class_list = {}
for t_a in range(0, cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES):
result = compute_result['perframe'](
cfg,
np.concatenate(list(gt_targets.values()), axis=0),
np.concatenate(list(pred_scores.values()), axis=0)[:, :, t_a],
)
logger.info('Action anticipation ({:.2f}s) perframe m{}: {:.5f}'.format(
(t_a + 1) / cfg.DATA.FPS * cfg.MODEL.LSTR.ANTICIPATION_SAMPLE_RATE,
cfg.DATA.METRICS, result['mean_AP']
))
maps_list.append(result['mean_AP'])
# per_class_AP = result['per_class_AP']
anticipation_time=(t_a + 1) / cfg.DATA.FPS * cfg.MODEL.LSTR.ANTICIPATION_SAMPLE_RATE
for class_name, ap in result['per_class_AP'].items():
if class_name not in per_class_list:
per_class_list[class_name] = []
per_class_list[class_name].append(ap)
logger.info(f'Action anticipation ({anticipation_time:.2f}s) perframe AP of {class_name}: {ap:.5f}')
logger.info('Action anticipation (mean) perframe m{}: {:.5f}'.format(
cfg.DATA.METRICS, np.mean(maps_list)
))
for class_name, ap_list in per_class_list.items():
logger.info(f'Action anticipation (mean) perframe AP of {class_name}: {np.mean(ap_list):.5f}')
# EDIT: save predictions!
for session in pred_scores.keys():
anticipation_score = pred_scores[session]
anticipation_dir = os.path.join(cfg.OUTPUT_DIR, 'pred')
if not os.path.exists(anticipation_dir):
os.makedirs(anticipation_dir)
np.save(osp.join(anticipation_dir, session+'.npy'), anticipation_score)
else:
result = compute_result['perframe'](
cfg,
np.concatenate(list(gt_targets.values()), axis=0),
np.concatenate(list(pred_scores.values()), axis=0),
)
logger.info('Action detection perframe m{}: {:.5f}'.format(
cfg.DATA.METRICS, result['mean_AP']
))
# segment-level evaluation on EK55/EK100
if cfg.DATA.DATA_NAME.startswith('EK'):
try:
from external.rulstm.RULSTM.utils import (
get_marginal_indexes, marginalize, softmax,
topk_accuracy_multiple_timesteps,
topk_recall_multiple_timesteps, tta)
except:
raise ModuleNotFoundError
if cfg.DATA.DATA_NAME == 'EK55':
path_to_data = 'external/rulstm/RULSTM/data/ek55/'
elif cfg.DATA.DATA_NAME == 'EK100':
path_to_data = 'external/rulstm/RULSTM/data/ek100/'
segment_list = pd.read_csv(osp.join(path_to_data, 'validation.csv'),
names=['id', 'video', 'start_f', 'end_f', 'verb', 'noun', 'action'],
skipinitialspace=True)
predictions, labels, ids = [], [], []
predictions_verb, predictions_noun = [], []
discarded_labels, discarded_ids = [], []
for segment in segment_list.iterrows():
start_step = int(np.floor(segment[1]['start_f'] / 30 * cfg.DATA.FPS))
vid = segment[1]['video'].strip()
preds = (pred_scores[vid][start_step][None, 1:, ::-1]).transpose(0, 2, 1)
if cfg.MODEL.LSTR.V_N_CLASSIFIER:
preds_v = (pred_scores_verb[vid][start_step][None, 1:, ::-1]).transpose(0, 2, 1)
preds_n = (pred_scores_noun[vid][start_step][None, 1:, ::-1]).transpose(0, 2, 1)
else:
preds_v = None
preds_n = None
if start_step >= cfg.MODEL.LSTR.ANTICIPATION_LENGTH:
# pt = []
# for t_a in range(0, cfg.MODEL.LSTR.ANTICIPATION_LENGTH):
# pt.append(pred_scores[vid][start_step - t_a, 1:, t_a][None, None, :])
# preds = np.concatenate(pt, axis=1)
predictions.append(preds)
if preds_v is not None:
predictions_verb.append(preds_v)
if preds_n is not None:
predictions_noun.append(preds_n)
labels.append(segment[1][['verb', 'noun', 'action']].values.astype(int))
ids.append(segment[1]['id'])
else:
discarded_labels.append(segment[1][['verb', 'noun', 'action']].values.astype(int))
discarded_ids.append(segment[1]['id'])
actions = pd.read_csv(osp.join(path_to_data, 'actions.csv'), index_col='id')
action_scores = np.concatenate(predictions)
if len(predictions_verb) > 0:
verb_scores = np.concatenate(predictions_verb)
else:
verb_scores = None
if len(predictions_noun) > 0:
noun_scores = np.concatenate(predictions_noun)
else:
noun_scores = None
labels = np.array(labels)
ids = np.array(ids)
vi = get_marginal_indexes(actions, 'verb')
ni = get_marginal_indexes(actions, 'noun')
pkl.dump({
'cfg': cfg,
'action_scores': action_scores,
'verb_scores': verb_scores,
'noun_scores': noun_scores,
}, open(osp.splitext(cfg.MODEL.CHECKPOINT)[0] + '.ek55_style.pkl', 'wb'))
action_probs = softmax(action_scores.reshape(-1, action_scores.shape[-1]))
# action_probs = action_probs / action_probs.sum(-1, keepdims=True)
print(action_probs.shape)
if verb_scores is None:
verb_scores = marginalize(action_probs, vi).reshape(action_scores.shape[0],
action_scores.shape[1],
-1)
else:
verb_scores = softmax(verb_scores.reshape(-1, verb_scores.shape[-1]))
verb_scores = verb_scores.reshape(action_scores.shape[0],
action_scores.shape[1],
-1)
if noun_scores is None:
noun_scores = marginalize(action_probs, ni).reshape(action_scores.shape[0],
action_scores.shape[1],
-1)
else:
noun_scores = softmax(noun_scores.reshape(-1, noun_scores.shape[-1]))
noun_scores = noun_scores.reshape(action_scores.shape[0],
action_scores.shape[1],
-1)
include_discarded = (cfg.DATA.DATA_NAME == 'EK100')
if include_discarded:
dlab = np.array(discarded_labels)
dislab = np.array(discarded_ids)
ids = np.concatenate([ids, dislab])
num_disc = len(dlab)
labels = np.concatenate([labels, dlab])
verb_scores = np.concatenate((verb_scores, np.zeros((num_disc, *verb_scores.shape[1:]))))
noun_scores = np.concatenate((noun_scores, np.zeros((num_disc, *noun_scores.shape[1:]))))
action_scores = np.concatenate((action_scores, np.zeros((num_disc, *action_scores.shape[1:]))))
verb_labels, noun_labels, action_labels = labels[:, 0], labels[:, 1], labels[:, 2]
verb_accuracies = topk_accuracy_multiple_timesteps(verb_scores, verb_labels)
noun_accuracies = topk_accuracy_multiple_timesteps(noun_scores, noun_labels)
action_accuracies = topk_accuracy_multiple_timesteps(action_scores, action_labels)
if cfg.DATA.DATA_NAME == 'EK55':
many_shot_verbs = pd.read_csv(osp.join(path_to_data, 'EPIC_many_shot_verbs.csv'))['verb_class'].values
many_shot_nouns = pd.read_csv(osp.join(path_to_data, 'EPIC_many_shot_nouns.csv'))['noun_class'].values
actions = pd.read_csv(osp.join(path_to_data, 'actions.csv'))
a_to_vn = {a[1]['id']: tuple(a[1][['verb', 'noun']].values)
for a in actions.iterrows()}
many_shot_actions = [a for a, (v, n) in a_to_vn.items()
if v in many_shot_verbs or n in many_shot_nouns]
verb_recalls = topk_recall_multiple_timesteps(verb_scores, verb_labels,
k=5, classes=many_shot_verbs)
noun_recalls = topk_recall_multiple_timesteps(noun_scores, noun_labels,
k=5, classes=many_shot_nouns)
action_recalls = topk_recall_multiple_timesteps(action_scores, action_labels,
k=5, classes=many_shot_actions)
all_accuracies = np.concatenate([verb_accuracies, noun_accuracies, action_accuracies,
verb_recalls, noun_recalls, action_recalls])
all_accuracies = all_accuracies[[0, 1, 6, 2, 3, 7, 4, 5, 8]]
indices = [
('Verb', 'Top-1 Accuracy'), ('Verb', 'Top-5 Accuracy'),
('Verb', 'Mean Top-5 Recall'),
('Noun', 'Top-1 Accuracy'), ('Noun', 'Top-5 Accuracy'),
('Noun', 'Mean Top-5 Recall'),
('Action', 'Top-1 Accuracy'), ('Action', 'Top-5 Accuracy'),
('Action', 'Mean Top-5 Recall'),
]
else:
overall_verb_recalls = topk_recall_multiple_timesteps(verb_scores, verb_labels, k=5)
overall_noun_recalls = topk_recall_multiple_timesteps(noun_scores, noun_labels, k=5)
overall_action_recalls = topk_recall_multiple_timesteps(action_scores, action_labels, k=5)
unseen = pd.read_csv(osp.join(path_to_data, 'validation_unseen_participants_ids.csv'), names=['id'], squeeze=True)
tail_verbs = pd.read_csv(osp.join(path_to_data, 'validation_tail_verbs_ids.csv'), names=['id'], squeeze=True)
tail_nouns = pd.read_csv(osp.join(path_to_data, 'validation_tail_nouns_ids.csv'), names=['id'], squeeze=True)
tail_actions = pd.read_csv(osp.join(path_to_data, 'validation_tail_actions_ids.csv'), names=['id'], squeeze=True)
unseen_bool_idx = pd.Series(ids).isin(unseen).values
tail_verbs_bool_idx = pd.Series(ids).isin(tail_verbs).values
tail_nouns_bool_idx = pd.Series(ids).isin(tail_nouns).values
tail_actions_bool_idx = pd.Series(ids).isin(tail_actions).values
tail_verb_recalls = topk_recall_multiple_timesteps(
verb_scores[tail_verbs_bool_idx], verb_labels[tail_verbs_bool_idx], k=5)
tail_noun_recalls = topk_recall_multiple_timesteps(
noun_scores[tail_nouns_bool_idx], noun_labels[tail_nouns_bool_idx], k=5)
tail_action_recalls = topk_recall_multiple_timesteps(
action_scores[tail_actions_bool_idx], action_labels[tail_actions_bool_idx], k=5)
unseen_verb_recalls = topk_recall_multiple_timesteps(
verb_scores[unseen_bool_idx], verb_labels[unseen_bool_idx], k=5)
unseen_noun_recalls = topk_recall_multiple_timesteps(
noun_scores[unseen_bool_idx], noun_labels[unseen_bool_idx], k=5)
unseen_action_recalls = topk_recall_multiple_timesteps(
action_scores[unseen_bool_idx], action_labels[unseen_bool_idx], k=5)
all_accuracies = np.concatenate(
[overall_verb_recalls, overall_noun_recalls, overall_action_recalls,
unseen_verb_recalls, unseen_noun_recalls, unseen_action_recalls,
tail_verb_recalls, tail_noun_recalls, tail_action_recalls]
) #9 x 8
indices = [
('Overall Mean Top-5 Recall', 'Verb'), ('Overall Mean Top-5 Recall', 'Noun'),
('Overall Mean Top-5 Recall', 'Action'),
('Unseen Mean Top-5 Recall', 'Verb'), ('Unseen Mean Top-5 Recall', 'Noun'),
('Unseen Mean Top-5 Recall', 'Action'),
('Tail Mean Top-5 Recall', 'Verb'), ('Tail Mean Top-5 Recall', 'Noun'),
('Tail Mean Top-5 Recall', 'Action'),
]
cc = np.linspace(1 / cfg.DATA.FPS * cfg.MODEL.LSTR.ANTICIPATION_LENGTH,
1 / cfg.DATA.FPS * cfg.MODEL.LSTR.ANTICIPATION_SAMPLE_RATE,
cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES, dtype=str)
scores = pd.DataFrame(all_accuracies * 100, columns=cc,
index=pd.MultiIndex.from_tuples(indices))
logger.info(scores)
tta_verb = tta(verb_scores, verb_labels)
tta_noun = tta(noun_scores, noun_labels)
tta_action = tta(action_scores, action_labels)
logger.info(f'\nMean TtA(5): VERB {tta_verb:0.2f} '
f'NOUN: {tta_noun:0.2f} ACTION: {tta_action:0.2f}')
def do_perframe_det_batch_inference(cfg, model, device, logger):
# Setup model to test mode
model.eval()
cfg.MODEL.CRITERIONS = [['MCE', {}]]
data_loader = torch.utils.data.DataLoader(
dataset=build_dataset(cfg, phase='test', tag='BatchInference'),
batch_size=cfg.DATA_LOADER.BATCH_SIZE,
num_workers=cfg.DATA_LOADER.NUM_WORKERS,
pin_memory=cfg.DATA_LOADER.PIN_MEMORY,
)
logger.info(f"phase: test) Inference on test_dataloader")
start = time.time()
batch_inference(cfg, model, device, logger, data_loader)
end = time.time() - start
logger.info(f"Elapsed time for function batch_inference: {end:.2f}s")
# data_loader = torch.utils.data.DataLoader(
# dataset=build_dataset(cfg, phase='train', tag='BatchInference'),
# batch_size=cfg.DATA_LOADER.BATCH_SIZE,
# num_workers=cfg.DATA_LOADER.NUM_WORKERS,
# pin_memory=cfg.DATA_LOADER.PIN_MEMORY,
# )
# logger.info(f"***** phase: train")
# batch_inference(cfg, model, device, logger, data_loader)
# # Collect scores and targets
# pred_scores = {}
# pred_scores_verb = {}
# pred_scores_noun = {}
# gt_targets = {}
# with torch.no_grad():
# pbar = tqdm(data_loader, desc='BatchInference')
# for batch_idx, data in enumerate(pbar, start=1):
# target = data[-4]
# if cfg.MODEL.LSTR.V_N_CLASSIFIER:
# target, verb_target, noun_target = target
# score = model(*[x.to(device) for x in data[:-4]])
# if cfg.MODEL.LSTR.V_N_CLASSIFIER:
# score, score_verb, score_noun = score
# score = score.cpu().numpy()
# score_verb = score_verb.cpu().numpy()
# score_noun = score_noun.cpu().numpy()
# cfg.DATA.NUM_VERBS = 126 if cfg.DATA.DATA_NAME == 'EK55' else 98
# cfg.DATA.NUM_NOUNS = 353 if cfg.DATA.DATA_NAME == 'EK55' else 301
# else:
# score = (score.cpu().numpy()
# if cfg.DATA.DATA_NAME.startswith('EK')
# else score.softmax(dim=-1).cpu().numpy())
# for bs, (session, query_indices, num_frames) in enumerate(zip(*data[-3:])):
# if session not in pred_scores:
# if cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES > 0:
# pred_scores[session] = np.zeros((num_frames, cfg.DATA.NUM_CLASSES, cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES))
# if cfg.MODEL.LSTR.V_N_CLASSIFIER:
# pred_scores_verb[session] = np.zeros((num_frames, cfg.DATA.NUM_VERBS, cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES))
# pred_scores_noun[session] = np.zeros((num_frames, cfg.DATA.NUM_NOUNS, cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES))
# else:
# pred_scores[session] = np.zeros((num_frames, cfg.DATA.NUM_CLASSES))
# if session not in gt_targets:
# gt_targets[session] = np.zeros((num_frames, cfg.DATA.NUM_CLASSES))
# if query_indices[0] in torch.arange(0, cfg.MODEL.LSTR.WORK_MEMORY_SAMPLE_RATE):
# if cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES > 0:
# for t_a in range(0, cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES):
# full_indices = torch.cat((query_indices,
# torch.arange(query_indices[-1] + cfg.MODEL.LSTR.WORK_MEMORY_SAMPLE_RATE,
# query_indices[-1] + t_a + 1 + cfg.MODEL.LSTR.WORK_MEMORY_SAMPLE_RATE)),
# dim=0)
# pred_scores[session][full_indices, :, t_a] = score[bs][:full_indices.shape[0]]
# if cfg.MODEL.LSTR.V_N_CLASSIFIER:
# pred_scores_verb[session][full_indices, :, t_a] = score_verb[bs][:full_indices.shape[0]]
# pred_scores_noun[session][full_indices, :, t_a] = score_noun[bs][:full_indices.shape[0]]
# gt_targets[session][query_indices] = target[bs][:query_indices.shape[0]]
# else:
# pred_scores[session][query_indices] = score[bs]
# gt_targets[session][query_indices] = target[bs]
# else:
# if cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES > 0:
# for t_a in range(0, cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES):
# if query_indices[-1] + t_a + 1 < num_frames:
# pred_scores[session][query_indices[-1] + t_a + 1, :, t_a] = score[bs][t_a - cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES]
# if cfg.MODEL.LSTR.V_N_CLASSIFIER:
# pred_scores_verb[session][query_indices[-1] + t_a + 1, :, t_a] = score_verb[bs][t_a - cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES]
# pred_scores_noun[session][query_indices[-1] + t_a + 1, :, t_a] = score_noun[bs][t_a - cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES]
# gt_targets[session][query_indices[-1]] = target[bs][-1]
# else:
# pred_scores[session][query_indices[-1]] = score[bs][-1]
# gt_targets[session][query_indices[-1]] = target[bs][-1]
# # Save scores and targets
# # pkl.dump({
# # 'cfg': cfg,
# # 'perframe_pred_scores': pred_scores,
# # 'perframe_gt_targets': gt_targets,
# # }, open(osp.splitext(cfg.MODEL.CHECKPOINT)[0] + '.pkl', 'wb'))
# # Compute results
# if cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES > 0:
# maps_list = []
# for t_a in range(0, cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES):
# result = compute_result['perframe'](
# cfg,
# np.concatenate(list(gt_targets.values()), axis=0),
# np.concatenate(list(pred_scores.values()), axis=0)[:, :, t_a],
# )
# logger.info('Action anticipation ({:.2f}s) perframe m{}: {:.5f}'.format(
# (t_a + 1) / cfg.DATA.FPS * cfg.MODEL.LSTR.ANTICIPATION_SAMPLE_RATE,
# cfg.DATA.METRICS, result['mean_AP']
# ))
# maps_list.append(result['mean_AP'])
# logger.info('Action anticipation (mean) perframe m{}: {:.5f}'.format(
# cfg.DATA.METRICS, np.mean(maps_list)
# ))
# else:
# result = compute_result['perframe'](
# cfg,
# np.concatenate(list(gt_targets.values()), axis=0),
# np.concatenate(list(pred_scores.values()), axis=0),
# )
# logger.info('Action detection perframe m{}: {:.5f}'.format(
# cfg.DATA.METRICS, result['mean_AP']
# ))
# # segment-level evaluation on EK55/EK100
# if cfg.DATA.DATA_NAME.startswith('EK'):
# try:
# from external.rulstm.RULSTM.utils import (get_marginal_indexes, marginalize, softmax,
# topk_accuracy_multiple_timesteps,
# topk_recall_multiple_timesteps,
# tta)
# except:
# raise ModuleNotFoundError
# if cfg.DATA.DATA_NAME == 'EK55':
# path_to_data = 'external/rulstm/RULSTM/data/ek55/'
# elif cfg.DATA.DATA_NAME == 'EK100':
# path_to_data = 'external/rulstm/RULSTM/data/ek100/'
# segment_list = pd.read_csv(osp.join(path_to_data, 'validation.csv'),
# names=['id', 'video', 'start_f', 'end_f', 'verb', 'noun', 'action'],
# skipinitialspace=True)
# predictions, labels, ids = [], [], []
# predictions_verb, predictions_noun = [], []
# discarded_labels, discarded_ids = [], []
# for segment in segment_list.iterrows():
# start_step = int(np.floor(segment[1]['start_f'] / 30 * cfg.DATA.FPS))
# vid = segment[1]['video'].strip()
# preds = (pred_scores[vid][start_step][None, 1:, ::-1]).transpose(0, 2, 1)
# if cfg.MODEL.LSTR.V_N_CLASSIFIER:
# preds_v = (pred_scores_verb[vid][start_step][None, 1:, ::-1]).transpose(0, 2, 1)
# preds_n = (pred_scores_noun[vid][start_step][None, 1:, ::-1]).transpose(0, 2, 1)
# else:
# preds_v = None
# preds_n = None
# if start_step >= cfg.MODEL.LSTR.ANTICIPATION_LENGTH:
# # pt = []
# # for t_a in range(0, cfg.MODEL.LSTR.ANTICIPATION_LENGTH):
# # pt.append(pred_scores[vid][start_step - t_a, 1:, t_a][None, None, :])
# # preds = np.concatenate(pt, axis=1)
# predictions.append(preds)
# if preds_v is not None:
# predictions_verb.append(preds_v)
# if preds_n is not None:
# predictions_noun.append(preds_n)
# labels.append(segment[1][['verb', 'noun', 'action']].values.astype(int))
# ids.append(segment[1]['id'])
# else:
# discarded_labels.append(segment[1][['verb', 'noun', 'action']].values.astype(int))
# discarded_ids.append(segment[1]['id'])
# actions = pd.read_csv(osp.join(path_to_data, 'actions.csv'), index_col='id')
# action_scores = np.concatenate(predictions)
# if len(predictions_verb) > 0:
# verb_scores = np.concatenate(predictions_verb)
# else:
# verb_scores = None
# if len(predictions_noun) > 0:
# noun_scores = np.concatenate(predictions_noun)
# else:
# noun_scores = None
# labels = np.array(labels)
# ids = np.array(ids)
# vi = get_marginal_indexes(actions, 'verb')
# ni = get_marginal_indexes(actions, 'noun')
# pkl.dump({
# 'cfg': cfg,
# 'action_scores': action_scores,
# 'verb_scores': verb_scores,
# 'noun_scores': noun_scores,
# }, open(osp.splitext(cfg.MODEL.CHECKPOINT)[0] + '.ek55_style.pkl', 'wb'))
# action_probs = softmax(action_scores.reshape(-1, action_scores.shape[-1]))
# # action_probs = action_probs / action_probs.sum(-1, keepdims=True)
# print(action_probs.shape)
# if verb_scores is None:
# verb_scores = marginalize(action_probs, vi).reshape(action_scores.shape[0],
# action_scores.shape[1],
# -1)
# else:
# verb_scores = softmax(verb_scores.reshape(-1, verb_scores.shape[-1]))
# verb_scores = verb_scores.reshape(action_scores.shape[0],
# action_scores.shape[1],
# -1)
# if noun_scores is None:
# noun_scores = marginalize(action_probs, ni).reshape(action_scores.shape[0],
# action_scores.shape[1],
# -1)
# else:
# noun_scores = softmax(noun_scores.reshape(-1, noun_scores.shape[-1]))
# noun_scores = noun_scores.reshape(action_scores.shape[0],
# action_scores.shape[1],
# -1)
# include_discarded = (cfg.DATA.DATA_NAME == 'EK100')
# if include_discarded:
# dlab = np.array(discarded_labels)
# dislab = np.array(discarded_ids)
# ids = np.concatenate([ids, dislab])
# num_disc = len(dlab)
# labels = np.concatenate([labels, dlab])
# verb_scores = np.concatenate((verb_scores, np.zeros((num_disc, *verb_scores.shape[1:]))))
# noun_scores = np.concatenate((noun_scores, np.zeros((num_disc, *noun_scores.shape[1:]))))
# action_scores = np.concatenate((action_scores, np.zeros((num_disc, *action_scores.shape[1:]))))
# verb_labels, noun_labels, action_labels = labels[:, 0], labels[:, 1], labels[:, 2]
# verb_accuracies = topk_accuracy_multiple_timesteps(verb_scores, verb_labels)
# noun_accuracies = topk_accuracy_multiple_timesteps(noun_scores, noun_labels)
# action_accuracies = topk_accuracy_multiple_timesteps(action_scores, action_labels)
# if cfg.DATA.DATA_NAME == 'EK55':
# many_shot_verbs = pd.read_csv(osp.join(path_to_data, 'EPIC_many_shot_verbs.csv'))['verb_class'].values
# many_shot_nouns = pd.read_csv(osp.join(path_to_data, 'EPIC_many_shot_nouns.csv'))['noun_class'].values
# actions = pd.read_csv(osp.join(path_to_data, 'actions.csv'))
# a_to_vn = {a[1]['id']: tuple(a[1][['verb', 'noun']].values)
# for a in actions.iterrows()}
# many_shot_actions = [a for a, (v, n) in a_to_vn.items()
# if v in many_shot_verbs or n in many_shot_nouns]
# verb_recalls = topk_recall_multiple_timesteps(verb_scores, verb_labels,
# k=5, classes=many_shot_verbs)
# noun_recalls = topk_recall_multiple_timesteps(noun_scores, noun_labels,
# k=5, classes=many_shot_nouns)
# action_recalls = topk_recall_multiple_timesteps(action_scores, action_labels,
# k=5, classes=many_shot_actions)
# all_accuracies = np.concatenate([verb_accuracies, noun_accuracies, action_accuracies,
# verb_recalls, noun_recalls, action_recalls])
# all_accuracies = all_accuracies[[0, 1, 6, 2, 3, 7, 4, 5, 8]]
# indices = [
# ('Verb', 'Top-1 Accuracy'), ('Verb', 'Top-5 Accuracy'),
# ('Verb', 'Mean Top-5 Recall'),
# ('Noun', 'Top-1 Accuracy'), ('Noun', 'Top-5 Accuracy'),
# ('Noun', 'Mean Top-5 Recall'),
# ('Action', 'Top-1 Accuracy'), ('Action', 'Top-5 Accuracy'),
# ('Action', 'Mean Top-5 Recall'),
# ]
# else:
# overall_verb_recalls = topk_recall_multiple_timesteps(verb_scores, verb_labels, k=5)
# overall_noun_recalls = topk_recall_multiple_timesteps(noun_scores, noun_labels, k=5)
# overall_action_recalls = topk_recall_multiple_timesteps(action_scores, action_labels, k=5)
# unseen = pd.read_csv(osp.join(path_to_data, 'validation_unseen_participants_ids.csv'), names=['id'], squeeze=True)
# tail_verbs = pd.read_csv(osp.join(path_to_data, 'validation_tail_verbs_ids.csv'), names=['id'], squeeze=True)
# tail_nouns = pd.read_csv(osp.join(path_to_data, 'validation_tail_nouns_ids.csv'), names=['id'], squeeze=True)
# tail_actions = pd.read_csv(osp.join(path_to_data, 'validation_tail_actions_ids.csv'), names=['id'], squeeze=True)
# unseen_bool_idx = pd.Series(ids).isin(unseen).values
# tail_verbs_bool_idx = pd.Series(ids).isin(tail_verbs).values
# tail_nouns_bool_idx = pd.Series(ids).isin(tail_nouns).values
# tail_actions_bool_idx = pd.Series(ids).isin(tail_actions).values
# tail_verb_recalls = topk_recall_multiple_timesteps(
# verb_scores[tail_verbs_bool_idx], verb_labels[tail_verbs_bool_idx], k=5)
# tail_noun_recalls = topk_recall_multiple_timesteps(
# noun_scores[tail_nouns_bool_idx], noun_labels[tail_nouns_bool_idx], k=5)
# tail_action_recalls = topk_recall_multiple_timesteps(
# action_scores[tail_actions_bool_idx], action_labels[tail_actions_bool_idx], k=5)
# unseen_verb_recalls = topk_recall_multiple_timesteps(
# verb_scores[unseen_bool_idx], verb_labels[unseen_bool_idx], k=5)
# unseen_noun_recalls = topk_recall_multiple_timesteps(
# noun_scores[unseen_bool_idx], noun_labels[unseen_bool_idx], k=5)
# unseen_action_recalls = topk_recall_multiple_timesteps(
# action_scores[unseen_bool_idx], action_labels[unseen_bool_idx], k=5)
# all_accuracies = np.concatenate(
# [overall_verb_recalls, overall_noun_recalls, overall_action_recalls,
# unseen_verb_recalls, unseen_noun_recalls, unseen_action_recalls,
# tail_verb_recalls, tail_noun_recalls, tail_action_recalls]
# ) #9 x 8
# indices = [
# ('Overall Mean Top-5 Recall', 'Verb'), ('Overall Mean Top-5 Recall', 'Noun'),
# ('Overall Mean Top-5 Recall', 'Action'),
# ('Unseen Mean Top-5 Recall', 'Verb'), ('Unseen Mean Top-5 Recall', 'Noun'),
# ('Unseen Mean Top-5 Recall', 'Action'),
# ('Tail Mean Top-5 Recall', 'Verb'), ('Tail Mean Top-5 Recall', 'Noun'),
# ('Tail Mean Top-5 Recall', 'Action'),
# ]
# cc = np.linspace(1 / cfg.DATA.FPS * cfg.MODEL.LSTR.ANTICIPATION_LENGTH,
# 1 / cfg.DATA.FPS * cfg.MODEL.LSTR.ANTICIPATION_SAMPLE_RATE,
# cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES, dtype=str)
# scores = pd.DataFrame(all_accuracies * 100, columns=cc,
# index=pd.MultiIndex.from_tuples(indices))
# logger.info(scores)
# tta_verb = tta(verb_scores, verb_labels)
# tta_noun = tta(noun_scores, noun_labels)
# tta_action = tta(action_scores, action_labels)
# logger.info(f'\nMean TtA(5): VERB {tta_verb:0.2f} '
# f'NOUN: {tta_noun:0.2f} ACTION: {tta_action:0.2f}')
# f'NOUN: {tta_noun:0.2f} ACTION: {tta_action:0.2f}')

View File

@@ -0,0 +1,2 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0

View File

@@ -0,0 +1,245 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import os
import os.path as osp
import time
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import wandb
from rekognition_online_action_detection.evaluation import compute_result
from tqdm import tqdm
def log_results(phase, cfg, compute_result, gt_targets, pred_scores, losses, data_loader, epoch=None, best_mAP=None, best_epoch=None):
result = compute_result['perframe'](cfg, gt_targets, pred_scores)
per_class_AP = result['mean_AP']
per_class_AP_log = ' '.join(f'{class_name}: {class_AP:.5f}' for class_name, class_AP in per_class_AP.items())
log = []
if epoch is not None:
log.append(f'\nEpoch {epoch:2}')
log.append(f'{phase} det_loss: {losses["det"] / len(data_loader.dataset):.5f} det_mAP: {result["mean_AP"]:.5f} {per_class_AP_log}')
if cfg.USE_WANDB:
wandb.log({
f"{phase.capitalize()} mAP": result['mean_AP'],
f"{phase.capitalize()} per_class_AP": per_class_AP,
f"{phase.capitalize()} Loss": losses['det'] / len(data_loader.dataset)
})
if cfg.MODEL.LSTR.V_N_CLASSIFIER:
for loss_type in ['verb', 'noun']:
if loss_type in losses:
log.append(f'{phase} {loss_type}_loss: {losses[loss_type] / len(data_loader.dataset):.5f}')
return log, result['mean_AP']
def do_perframe_det_train(cfg,
data_loaders,
model,
criterion,
optimizer,
scheduler,
device,
checkpointer,
logger):
# Setup model on multiple GPUs
if torch.cuda.device_count() > 1:
model = nn.DataParallel(model)
best_mAP, best_epoch = 0, 0
best_per_class_AP_log = None
for epoch in range(cfg.SOLVER.START_EPOCH, cfg.SOLVER.START_EPOCH + cfg.SOLVER.NUM_EPOCHS):
# Reset
det_losses = {phase: 0.0 for phase in cfg.SOLVER.PHASES}
pred_losses = {phase: 0.0 for phase in cfg.SOLVER.PHASES}
verb_losses = {phase: 0.0 for phase in cfg.SOLVER.PHASES}
noun_losses = {phase: 0.0 for phase in cfg.SOLVER.PHASES}
det_pred_scores = []
det_gt_targets = []
aa_pred_scores = []
aa_gt_targets = []
train_det_pred_scores = []
train_det_gt_targets = []
train_aa_pred_scores = []
train_aa_gt_targets = []
start = time.time()
for phase in cfg.SOLVER.PHASES:
training = phase == 'train'
model.train(training)
with torch.set_grad_enabled(training):
pbar = tqdm(data_loaders[phase],
desc='{}ing epoch {}'.format(phase.capitalize(), epoch))
for batch_idx, data in enumerate(pbar, start=1):
batch_size = data[0].shape[0]
# batch_size 16
# data[0]: visual_input, [16, 680, 2048]
# [BATCH_SIZE, 640 + 40 (LONG_MEMORY_NUM_SAMPLES + WORK_MEMORY_NUM_SAMPLES), FEATURE_SIZE]
# this if statement is for the case of EK100 dataset
if cfg.MODEL.LSTR.V_N_CLASSIFIER:
det_target, verb_target, noun_target = data[-1]
det_target = det_target.to(device)
verb_target = verb_target.to(device)
noun_target = noun_target.to(device)
else:
det_target = data[-1].to(device)
loss_names = list(zip(*cfg.MODEL.CRITERIONS))[0]
if 'PRED_FUTURE' in list(zip(*cfg.MODEL.CRITERIONS))[0]:
det_score, feat_out, feat_ori = model(*[x.to(device) for x in data[:-1]])
else:
det_score = model(*[x.to(device) for x in data[:-1]])
if cfg.MODEL.LSTR.V_N_CLASSIFIER:
det_score, verb_score, noun_score = det_score
else:
verb_score, noun_score = None, None
if cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES > 0:
aa_score = det_score[:, -cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES:, :]
# [16, 10, 3]
aa_target = det_target[:, -cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES:, :]
if cfg.MODEL.LSTR.LOSS_ANTICIPATE_ONLY:
det_score = det_score[:, -cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES:, :]
det_target = det_target[:, -cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES:, :]
# det_score: [16, 50, 3], aa_score: [16, 10, 3]
# det_score can be different when aa_score
det_score = det_score.reshape(-1, cfg.DATA.NUM_CLASSES)
det_target = det_target.reshape(-1, cfg.DATA.NUM_CLASSES)
assert len(det_score.shape) == 2
det_loss = criterion[loss_names[0]](det_score, det_target)
det_losses[phase] += det_loss.item() * batch_size
if 'PRED_FUTURE' in list(zip(*cfg.MODEL.CRITERIONS))[0]:
pred_loss = criterion['PRED_FUTURE'](feat_out, feat_ori)
pred_losses[phase] += pred_loss.item() * batch_size
if cfg.MODEL.LSTR.V_N_CLASSIFIER:
if cfg.MODEL.LSTR.LOSS_ANTICIPATE_ONLY:
verb_score = verb_score[:, -cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES:, :]
verb_target = verb_target[:, -cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES:, :]
noun_score = noun_score[:, -cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES:, :]
noun_target = noun_target[:, -cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES:, :]
verb_score = verb_score.reshape(-1, verb_score.shape[-1])
noun_score = noun_score.reshape(-1, noun_score.shape[-1])
verb_target = verb_target.reshape(-1, verb_target.shape[-1])
noun_target = noun_target.reshape(-1, noun_target.shape[-1])
verb_loss = criterion['MCE'](verb_score, verb_target)
noun_loss = criterion['MCE'](noun_score, noun_target)
verb_losses[phase] += verb_loss.item() * batch_size
noun_losses[phase] += noun_loss.item() * batch_size
# Output log for current batch
pbar.set_postfix({
'lr': '{:.7f}'.format(scheduler.get_last_lr()[0]),
'det_loss': '{:.5f}'.format(det_loss.item()),
})
if training:
optimizer.zero_grad()
loss = det_loss
if 'PRED_FUTURE' in list(zip(*cfg.MODEL.CRITERIONS))[0]:
loss += pred_loss
if cfg.MODEL.LSTR.V_N_CLASSIFIER:
loss += noun_loss + verb_loss
if loss.item() != 0:
loss.backward()
optimizer.step()
scheduler.step()
if cfg.USE_WANDB:
wandb.log({"Learning Rate": scheduler.get_last_lr()[0]})
wandb.log({"Train Loss": det_loss.item()})
det_score = det_score.softmax(dim=1).cpu().tolist()
det_target = det_target.cpu().tolist()
train_det_pred_scores.extend(det_score)
train_det_gt_targets.extend(det_target)
if cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES > 0:
aa_score = aa_score.softmax(dim=1).cpu().tolist()
aa_target = aa_target.cpu().tolist()
train_aa_pred_scores.extend(aa_score)
train_aa_gt_targets.extend(aa_target)
else:
# Prepare for evaluation
det_score = det_score.softmax(dim=1).cpu().tolist()
det_target = det_target.cpu().tolist()
det_pred_scores.extend(det_score)
det_gt_targets.extend(det_target)
if cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES > 0:
aa_score = aa_score.softmax(dim=1).cpu().tolist()
aa_target = aa_target.cpu().tolist()
aa_pred_scores.extend(aa_score)
aa_gt_targets.extend(aa_target)
end = time.time()
# After the phase loop, compute and log results
for phase in cfg.SOLVER.PHASES:
if phase == 'train':
gt_targets = train_det_gt_targets
pred_scores = train_det_pred_scores
else: # 'test'
gt_targets = det_gt_targets
pred_scores = det_pred_scores
result = compute_result['perframe'](cfg, gt_targets, pred_scores)
per_class_AP = result['per_class_AP']
log = []
log.append(f'\nEpoch {epoch:2}')
log.append(f'{phase} det_loss: {det_losses[phase] / len(data_loaders[phase].dataset):.5f} det_mAP: {result["mean_AP"]:.5f}')
per_class_AP_log = ' '.join(f'{class_name}: {class_AP:.5f}' for class_name, class_AP in per_class_AP.items())
log.append(f'Per-class AP: {per_class_AP_log}')
if cfg.USE_WANDB:
wandb.log({
f"{phase.capitalize()} mAP": result['mean_AP'],
f"{phase.capitalize()} per_class_AP": per_class_AP,
f"{phase.capitalize()} Loss": det_losses[phase] / len(data_loaders[phase].dataset)
})
if cfg.MODEL.LSTR.V_N_CLASSIFIER:
log.append(f'{phase} verb_loss: {verb_losses[phase] / len(data_loaders[phase].dataset):.5f}')
log.append(f'{phase} noun_loss: {noun_losses[phase] / len(data_loaders[phase].dataset):.5f}')
if phase == 'test':
if result['mean_AP'] > best_mAP:
best_mAP = result['mean_AP']
best_epoch = epoch
best_per_class_AP_log = per_class_AP_log
checkpointer.save_best(epoch, model, optimizer)
log.append(f'\nbest epoch: {best_epoch:2} best det_mAP: {best_mAP:.5f} best per-class AP: {best_per_class_AP_log}\n')
logger.info(' | '.join(log))
log.append(f'running time: {end - start:.2f} sec')
logger.info(' | '.join(log))
# Save checkpoint for model and optimizer
if cfg.SOLVER.SAVE_EVERY_EPOCHS > 0 and epoch % cfg.SOLVER.SAVE_EVERY_EPOCHS == 0:
checkpointer.save(epoch, model, optimizer)
# Shuffle dataset for next epoch
data_loaders['train'].dataset.shuffle()

View File

@@ -0,0 +1,330 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import os
import os.path as osp
import time
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import wandb
from rekognition_online_action_detection.evaluation import compute_result
from tqdm import tqdm
def log_results(phase, cfg, compute_result, gt_targets, pred_scores, losses, data_loader, epoch=None, best_mAP=None, best_epoch=None):
result = compute_result['perframe'](cfg, gt_targets, pred_scores)
per_class_AP = result['mean_AP']
per_class_AP_log = ' '.join(f'{class_name}: {class_AP:.5f}' for class_name, class_AP in per_class_AP.items())
log = []
if epoch is not None:
log.append(f'\nEpoch {epoch:2}')
log.append(f'{phase} det_loss: {losses["det"] / len(data_loader.dataset):.5f} det_mAP: {result["mean_AP"]:.5f} {per_class_AP_log}')
if cfg.USE_WANDB:
wandb.log({
f"{phase.capitalize()} mAP": result['mean_AP'],
f"{phase.capitalize()} per_class_AP": per_class_AP,
f"{phase.capitalize()} Loss": losses['det'] / len(data_loader.dataset)
})
if cfg.MODEL.LSTR.V_N_CLASSIFIER:
for loss_type in ['verb', 'noun']:
if loss_type in losses:
log.append(f'{phase} {loss_type}_loss: {losses[loss_type] / len(data_loader.dataset):.5f}')
return log, result['mean_AP']
def do_perframe_det_train_rnn(cfg,
data_loaders,
model,
criterion,
optimizer,
scheduler,
device,
checkpointer,
logger):
# Setup model on multiple GPUs
if torch.cuda.device_count() > 1:
model = nn.DataParallel(model)
best_mAP, best_epoch = 0, 0
best_per_class_AP_log = None
for epoch in range(cfg.SOLVER.START_EPOCH, cfg.SOLVER.START_EPOCH + cfg.SOLVER.NUM_EPOCHS):
# Reset
det_losses = {phase: 0.0 for phase in cfg.SOLVER.PHASES}
pred_losses = {phase: 0.0 for phase in cfg.SOLVER.PHASES}
det_pred_scores = []
det_gt_targets = []
aa_pred_scores = []
aa_gt_targets = []
train_det_pred_scores = []
train_det_gt_targets = []
train_aa_pred_scores = []
train_aa_gt_targets = []
start = time.time()
for phase in cfg.SOLVER.PHASES:
training = phase == 'train'
model.train(training)
with torch.set_grad_enabled(training):
pbar = tqdm(data_loaders[phase],
desc='{}ing epoch {}'.format(phase.capitalize(), epoch))
# __getitem in LSTR:
# return (fusion_visual_inputs, fusion_motion_inputs,
# fusion_object_inputs, memory_key_padding_mask, target)
# __getitem__ in RNN:
# vid, rgb_input, flow_input, text_input, target, ant_target
for batch_idx, data in enumerate(pbar, start=1):
batch_size = data[1].shape[0]
det_target = data[-2].to(device)
aa_target = data[-1].to(device)
loss_names = list(zip(*cfg.MODEL.CRITERIONS))[0]
assert 'PRED_FUTURE' not in list(zip(*cfg.MODEL.CRITERIONS))[0]
# if 'PRED_FUTURE' in list(zip(*cfg.MODEL.CRITERIONS))[0]:
# det_score, feat_out, feat_ori = model(*[x.to(device) for x in data[:-1]])
# else:
# det_score = model(*[x.to(device) for x in data[1:4]])
out_dict = model(*[x.to(device) for x in data[1:4]])
# out_dict['logits']: [B, window_size, num_classes]
# out_dict['anticipation_logits']: [B, window_size, ant_len, num_classes]
# [workmem_len + ant_len, batch_size, self.d_model]: [50, 16, 1024]
det_score = out_dict['logits']
det_score = det_score
if cfg.MODEL.MINIROAD.ANTICIPATION_LENGTH > 0:
aa_score = out_dict['anticipation_logits'] # [B, window_size, anticipation_length, num_class]
aa_score = aa_score[:, -1, :, :].reshape(-1, cfg.DATA.NUM_CLASSES)
aa_target = aa_target.reshape(-1, cfg.DATA.NUM_CLASSES)
det_loss = criterion[loss_names[0]](aa_score, aa_target)
anticipation_logits = out_dict['anticipation_logits']
else:
det_score = det_score.reshape(-1, cfg.DATA.NUM_CLASSES)
det_target = det_target.reshape(-1, cfg.DATA.NUM_CLASSES)
det_loss = criterion[loss_names[0]](det_score, det_target)
det_loss = criterion[loss_names[0]](det_score, det_target)
# if cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES > 0:
# aa_score = det_score[:, -cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES:, :]
# # [16, 10, 3]
# aa_target = det_target[:, -cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES:, :]
# if cfg.MODEL.LSTR.LOSS_ANTICIPATE_ONLY:
# det_score = det_score[:, -cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES:, :]
# det_target = det_target[:, -cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES:, :]
# det_score: [16, 50, 3], aa_score: [16, 10, 3]
# det_score can be different when aa_score
det_losses[phase] += det_loss.item() * batch_size
# Output log for current batch
pbar.set_postfix({
'lr': '{:.7f}'.format(scheduler.get_last_lr()[0]),
'det_loss': '{:.5f}'.format(det_loss.item()),
})
if training:
optimizer.zero_grad()
loss = det_loss
if 'PRED_FUTURE' in list(zip(*cfg.MODEL.CRITERIONS))[0]:
loss += pred_loss
if cfg.MODEL.LSTR.V_N_CLASSIFIER:
loss += noun_loss + verb_loss
if loss.item() != 0:
loss.backward()
optimizer.step()
scheduler.step()
if cfg.USE_WANDB:
wandb.log({"Learning Rate": scheduler.get_last_lr()[0]})
wandb.log({"Train Loss": det_loss.item()})
det_score = det_score.softmax(dim=1).cpu().tolist()
det_target = det_target.cpu().tolist()
train_det_pred_scores.extend(det_score)
train_det_gt_targets.extend(det_target)
# len(train_det_pred_scores) + 16
if cfg.MODEL.MINIROAD.ANTICIPATION_LENGTH > 0:
aa_score = aa_score.softmax(dim=1).cpu().tolist()
aa_target = aa_target.cpu().tolist()
train_aa_pred_scores.extend(aa_score)
train_aa_gt_targets.extend(aa_target)
else:
# Prepare for evaluation
det_score = det_score.softmax(dim=1).cpu().tolist()
det_target = det_target.cpu().tolist()
det_pred_scores.extend(det_score)
det_gt_targets.extend(det_target)
if cfg.MODEL.MINIROAD.ANTICIPATION_LENGTH > 0:
aa_score = aa_score.softmax(dim=1).cpu().tolist()
aa_target = aa_target.cpu().tolist()
aa_pred_scores.extend(aa_score)
aa_gt_targets.extend(aa_target)
# print(f'len(det_score): {len(det_score)}, len(det_pred_scores): {len(det_pred_scores)}')
# print(f'len(aa_score): {len(aa_score)}, len(aa_pred_scores): {len(aa_pred_scores)}')
end = time.time()
# After the phase loop, compute and log results
for phase in cfg.SOLVER.PHASES:
if phase == 'train':
gt_targets = train_det_gt_targets
pred_scores = train_det_pred_scores
else: # 'test'
gt_targets = det_gt_targets
pred_scores = det_pred_scores
result = compute_result['perframe'](cfg, gt_targets, pred_scores)
per_class_AP = result['per_class_AP']
log = []
log.append(f'\nEpoch {epoch:2}')
log.append(f'{phase} det_loss: {det_losses[phase] / len(data_loaders[phase].dataset):.5f} det_mAP: {result["mean_AP"]:.5f}')
per_class_AP_log = ' '.join(f'{class_name}: {class_AP:.5f}' for class_name, class_AP in per_class_AP.items())
log.append(f'Per-class AP: {per_class_AP_log}')
if cfg.USE_WANDB:
wandb.log({
f"{phase.capitalize()} mAP": result['mean_AP'],
f"{phase.capitalize()} per_class_AP": per_class_AP,
f"{phase.capitalize()} Loss": det_losses[phase] / len(data_loaders[phase].dataset)
})
if cfg.MODEL.LSTR.V_N_CLASSIFIER:
log.append(f'{phase} verb_loss: {verb_losses[phase] / len(data_loaders[phase].dataset):.5f}')
log.append(f'{phase} noun_loss: {noun_losses[phase] / len(data_loaders[phase].dataset):.5f}')
if phase == 'test':
if result['mean_AP'] > best_mAP:
best_mAP = result['mean_AP']
best_epoch = epoch
best_per_class_AP_log = per_class_AP_log
checkpointer.save_best(epoch, model, optimizer)
log.append(f'\nbest epoch: {best_epoch:2} best det_mAP: {best_mAP:.5f} best per-class AP: {best_per_class_AP_log}\n')
logger.info(' | '.join(log))
log.append(f'running time: {end - start:.2f} sec')
logger.info(' | '.join(log))
# Save checkpoint for model and optimizer
if cfg.SOLVER.SAVE_EVERY_EPOCHS > 0 and epoch % cfg.SOLVER.SAVE_EVERY_EPOCHS == 0:
checkpointer.save(epoch, model, optimizer)
# Shuffle dataset for next epoch
data_loaders['train'].dataset.shuffle()
# @TRAINER.register("OAD")
def train_one_epoch(trainloader, model, criterion, optimizer, scaler, epoch, use_wandb = False, writer=None, scheduler=None):
epoch_loss = 0
for it, (vid_name, rgb_input, flow_input, target) in enumerate(tqdm(trainloader, desc=f'Epoch:{epoch} Training', postfix=f'lr: {optimizer.param_groups[0]["lr"]:.7f}')):
rgb_input, flow_input, target = rgb_input.cuda(), flow_input.cuda(), target.cuda()
model.train()
if scaler != None:
with torch.cuda.amp.autocast():
out_dict = model(rgb_input, flow_input)
loss = criterion(out_dict, target)
optimizer.zero_grad(set_to_none=True)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
else:
out_dict = model(rgb_input, flow_input)
loss = criterion(out_dict, target)
optimizer.zero_grad(set_to_none=True)
loss.backward()
optimizer.step()
epoch_loss += loss.item()
if use_wandb == True:
wandb.log({"Train Loss": loss.item()})
if writer != None:
writer.add_scalar("Train Loss", loss.item(), it+epoch*len(trainloader))
return epoch_loss
# @TRAINER.register("OADTR")
def train_one_epoch_oadtr(trainloader, model, criterion, optimizer, scaler, epoch, use_wandb = False, writer=None, scheduler=None):
epoch_loss = 0
for it, (vid_name, rgb_input, flow_input, enc_target, distance_target, class_h_target, dec_target) in enumerate(tqdm(trainloader, desc=f'Epoch:{epoch} Training', postfix=f'lr: {optimizer.param_groups[0]["lr"]:.7f}')):
rgb_input, flow_input, enc_target, distance_target, class_h_target = rgb_input.cuda(), flow_input.cuda(), enc_target.cuda(), distance_target.cuda(), class_h_target.cuda()
model.train()
if scaler != None:
with torch.cuda.amp.autocast():
out_dict = model(rgb_input, flow_input)
loss = criterion(out_dict, enc_target, dec_target)
optimizer.zero_grad(set_to_none=True)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
else:
out_dict = model(rgb_input, flow_input)
loss = criterion(out_dict, enc_target, dec_target)
optimizer.zero_grad(set_to_none=True)
loss.backward()
optimizer.step()
epoch_loss += loss.item()
if use_wandb == True:
wandb.log({"Train Loss": loss.item()})
if writer != None:
writer.add_scalar("Train Loss", loss.item(), it+epoch*len(trainloader))
return epoch_loss
# @TRAINER.register("ANTICIPATION")
def ant_train_one_epoch(trainloader, model, criterion, optimizer, scaler, epoch, use_wandb = False, writer=None, scheduler=None):
epoch_loss = 0
for it, (vid_name, rgb_input, flow_input, text_input, target, ant_target) in enumerate(tqdm(trainloader, desc=f'Epoch:{epoch} Training', postfix=f'lr: {optimizer.param_groups[0]["lr"]:.7f}')):
rgb_input, flow_input, text_input, target, ant_target = rgb_input.cuda(), flow_input.cuda(), text_input.cuda(), target.cuda(), ant_target.cuda()
model.train()
if scaler != None:
with torch.cuda.amp.autocast():
out_dict = model(rgb_input, flow_input, text_input)
loss = criterion(out_dict, target, ant_target)
optimizer.zero_grad(set_to_none=True)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
else:
out_dict = model(rgb_input, flow_input, text_input)
loss = criterion(out_dict, target, ant_target)
optimizer.zero_grad(set_to_none=True)
loss.backward()
optimizer.step()
epoch_loss += loss.item()
if use_wandb == True:
wandb.log({"Train Loss": loss.item()})
if writer != None:
writer.add_scalar("Train Loss", loss.item(), it+epoch*len(trainloader))
return epoch_loss

View File

@@ -0,0 +1,39 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
from rekognition_online_action_detection.utils.registry import Registry
TRAINERS = Registry()
INFERENCES = Registry()
def do_train(cfg,
data_loaders,
model,
criterion,
optimizer,
scheduler,
device,
checkpointer,
logger):
return TRAINERS[cfg.MODEL.MODEL_NAME](
cfg,
data_loaders,
model,
criterion,
optimizer,
scheduler,
device,
checkpointer,
logger)
def do_inference(cfg,
model,
device,
logger):
return INFERENCES[cfg.MODEL.MODEL_NAME](
cfg,
model,
device,
logger)

View File

@@ -0,0 +1,2 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0

View File

@@ -0,0 +1,417 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import os.path as osp
from bisect import bisect_right
import time
import pickle as pkl
import torch
import numpy as np
from rekognition_online_action_detection.evaluation import compute_result
from ..base_inferences.perframe_det_batch_inference import do_perframe_det_batch_inference
from ..engines import INFERENCES as registry
@registry.register('LSTR')
def do_lstr_batch_inference(cfg,
model,
device,
logger):
if cfg.MODEL.LSTR.INFERENCE_MODE == 'stream':
do_lstr_stream_inference(cfg,
model,
device,
logger)
else:
do_perframe_det_batch_inference(cfg,
model,
device,
logger)
def do_lstr_stream_inference(cfg, model, device, logger):
# Setup model to test mode
model.eval()
# Collect scores and targets
pred_scores = []
gt_targets = []
def to_device(x, dtype=np.float32):
return torch.as_tensor(x.astype(dtype)).unsqueeze(0).to(device)
long_memory_length = cfg.MODEL.LSTR.LONG_MEMORY_LENGTH
long_memory_sample_rate = cfg.MODEL.LSTR.LONG_MEMORY_SAMPLE_RATE
long_memory_num_samples = cfg.MODEL.LSTR.LONG_MEMORY_NUM_SAMPLES
work_memory_length = cfg.MODEL.LSTR.WORK_MEMORY_LENGTH
work_memory_sample_rate = cfg.MODEL.LSTR.WORK_MEMORY_SAMPLE_RATE
work_memory_num_samples = cfg.MODEL.LSTR.WORK_MEMORY_NUM_SAMPLES
# if len(cfg.DATA.TEST_SESSION_SET) != 1:
# raise RuntimeError('Only support testing one video each time for stream inference, will fix later')
pred_scores_all = {}
gt_targets_all = {}
with torch.no_grad():
for session_idx, session in enumerate(cfg.DATA.TEST_SESSION_SET):
model.clear_cache()
if cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES > 0:
pred_scores = [[] for _ in range(cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES)]
else:
pred_scores = []
gt_targets = []
visual_inputs = np.load(osp.join(cfg.DATA.DATA_ROOT, cfg.INPUT.VISUAL_FEATURE, session + '.npy'), mmap_mode='r')
motion_inputs = np.load(osp.join(cfg.DATA.DATA_ROOT, cfg.INPUT.MOTION_FEATURE, session + '.npy'), mmap_mode='r')
object_inputs = np.load(osp.join(cfg.DATA.DATA_ROOT, cfg.INPUT.OBJECT_FEATURE, session + '.npy'), mmap_mode='r')
target = np.load(osp.join(cfg.DATA.DATA_ROOT, cfg.INPUT.TARGET_PERFRAME, session + '.npy'))
start_time = time.time()
for work_start, work_end in zip(range(0, target.shape[0] + 1),
range(work_memory_length, target.shape[0] + 1)):
# Get target
# target = target[::work_memory_sample_rate]
# Get work memory
work_indices = np.arange(work_start, work_end).clip(0)
work_indices = work_indices[::work_memory_sample_rate]
work_visual_inputs = to_device(visual_inputs[work_indices])
work_motion_inputs = to_device(motion_inputs[work_indices])
work_object_inputs = to_device(object_inputs[work_indices])
# Get long memory
PRECISE = True
if PRECISE:
long_end = work_start - long_memory_sample_rate
if long_end < 0:
long_indices = [0 for _ in range(long_memory_num_samples)]
long_indices_set = [long_indices for _ in range(long_memory_sample_rate)]
long_visual_inputs = to_device(visual_inputs[long_indices])
long_motion_inputs = to_device(motion_inputs[long_indices])
long_object_inputs = to_device(object_inputs[long_indices])
else:
long_indices = long_indices_set[long_end % long_memory_sample_rate][1:] + [long_end]
long_indices_set[long_end % long_memory_sample_rate] = long_indices
long_visual_inputs = to_device(visual_inputs[[long_end]])
long_motion_inputs = to_device(motion_inputs[[long_end]])
long_object_inputs = to_device(object_inputs[[long_end]])
else:
long_end = work_start - 1
if long_end == -1:
long_indices = [0 for _ in range(long_memory_num_samples)]
long_visual_inputs = to_device(visual_inputs[long_indices])
long_motion_inputs = to_device(motion_inputs[long_indices])
long_object_inputs = to_device(object_inputs[long_indices])
elif long_end % long_memory_sample_rate == 0:
long_indices = long_indices[1:] + [long_end]
long_visual_inputs = to_device(visual_inputs[[long_end]])
long_motion_inputs = to_device(motion_inputs[[long_end]])
long_object_inputs = to_device(object_inputs[[long_end]])
else:
long_visual_inputs = None
long_motion_inputs = None
long_object_inputs = None
# Get memory key padding mask
memory_key_padding_mask = np.zeros(len(long_indices))
last_zero = bisect_right(long_indices, 0) - 1
if last_zero > 0:
memory_key_padding_mask[:last_zero] = float('-inf')
memory_key_padding_mask = torch.as_tensor(memory_key_padding_mask.astype(np.float32)).unsqueeze(0).to(device)
score = model.stream_inference(
long_visual_inputs,
long_motion_inputs,
long_object_inputs,
work_visual_inputs,
work_motion_inputs,
work_object_inputs,
memory_key_padding_mask,
cache_num=long_memory_sample_rate if PRECISE else 1,
cache_id=long_end % long_memory_sample_rate if PRECISE else 0)[0]
if cfg.DATA.DATA_NAME.startswith('EK'):
score = score.cpu().numpy()
else:
score = score.softmax(dim=-1).cpu().numpy()
if cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES > 0:
if work_start == 0:
upsample_score = score[:-cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES].repeat(work_memory_sample_rate, axis=0)
upsample_score = upsample_score[work_start:work_end-1]
anticipate_score = score[-cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES:]
# coarse but sufficient
for t_a in range(0, cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES):
combined_score = np.concatenate((upsample_score, anticipate_score[:t_a + 1]),
axis=0)
pred_scores[t_a].extend(list(combined_score))
gt_targets.extend(list(target[:work_end-1]))
else:
for t_a in range(0, cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES):
pred_scores[t_a].append(list(score[t_a - cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES]))
gt_targets.append(list(target[work_end - 1]))
else:
if work_start == 0:
gt_targets.extend(list(target[:work_end]))
pred_scores.extend(list(score))
else:
gt_targets.append(list(target[work_end - 1]))
pred_scores.append(list(score[-1]))
end_time = time.time()
logger.info('Running time: {:.3f} seconds'.format(end_time - start_time))
if cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES > 0:
for t_a in range(0, cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES):
result = compute_result['perframe'](
cfg,
gt_targets,
pred_scores[t_a][:-1 - t_a],
)
sec = (t_a + 1) / cfg.DATA.FPS * cfg.MODEL.LSTR.ANTICIPATION_SAMPLE_RATE
logger.info('mAP of video ({:.2f}s) {}: {:.5f}'.format(sec, session, result['mean_AP']))
else:
result = compute_result['perframe'](
cfg,
gt_targets,
pred_scores,
)
logger.info('mAP of video {}: {:.5f}'.format(session, result['mean_AP']))
gt_targets_all[session] = np.array(gt_targets)
if cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES > 0:
for t_a in range(0, cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES):
pred_scores[t_a] = np.array(pred_scores[t_a][:- 1 - t_a])
pred_scores_all[session] = np.stack(pred_scores, axis=0).transpose(1, 2, 0)
pred_scores_all[session] = pred_scores_all[session]
else:
pred_scores_all[session] = np.array(pred_scores)
# pkl.dump({
# 'cfg': cfg,
# 'perframe_pred_scores': pred_scores_all,
# 'perframe_gt_targets': gt_targets_all,
# }, open(osp.splitext(cfg.MODEL.CHECKPOINT)[0] + '.stream.pkl', 'wb'))
if cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES > 0:
maps_list = []
for t_a in range(0, cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES):
result = compute_result['perframe'](
cfg,
np.concatenate(list(gt_targets_all.values()), axis=0),
np.concatenate(list(pred_scores_all.values()), axis=0)[:, :, t_a],
)
logger.info('Action anticipation ({:.2f}s) perframe m{}: {:.5f}'.format(
(t_a + 1) / cfg.DATA.FPS * cfg.MODEL.LSTR.ANTICIPATION_SAMPLE_RATE,
cfg.DATA.METRICS, result['mean_AP']
))
maps_list.append(result['mean_AP'])
logger.info('Action anticipation (mean) perframe m{}: {:.5f}'.format(
cfg.DATA.METRICS, np.mean(maps_list)
))
else:
result = compute_result['perframe'](
cfg,
np.concatenate(list(gt_targets_all.values()), axis=0),
np.concatenate(list(pred_scores_all.values()), axis=0),
)
logger.info('Action detection perframe m{}: {:.5f}'.format(
cfg.DATA.METRICS, result['mean_AP']
))
gt_targets = gt_targets_all
pred_scores = pred_scores_all
pred_scores_verb = {}
pred_scores_noun = {}
# segment-level evaluation on EK55/EK100
if cfg.DATA.DATA_NAME.startswith('EK'):
try:
import pandas as pd
from external.rulstm.RULSTM.utils import (get_marginal_indexes, marginalize, softmax,
topk_accuracy_multiple_timesteps,
topk_recall_multiple_timesteps,
tta)
except:
raise ModuleNotFoundError
if cfg.DATA.DATA_NAME == 'EK55':
path_to_data = 'external/rulstm/RULSTM/data/ek55/'
elif cfg.DATA.DATA_NAME == 'EK100':
path_to_data = 'external/rulstm/RULSTM/data/ek100/'
segment_list = pd.read_csv(osp.join(path_to_data, 'validation.csv'),
names=['id', 'video', 'start_f', 'end_f', 'verb', 'noun', 'action'],
skipinitialspace=True)
predictions, labels, ids = [], [], []
predictions_verb, predictions_noun = [], []
discarded_labels, discarded_ids = [], []
for segment in segment_list.iterrows():
start_step = int(np.floor(segment[1]['start_f'] / 30 * cfg.DATA.FPS))
vid = segment[1]['video'].strip()
preds = (pred_scores[vid][start_step][None, 1:, ::-1]).transpose(0, 2, 1)
if cfg.MODEL.LSTR.V_N_CLASSIFIER:
raise NotImplementedError(
'For TeSTra with decomposed classifier (V+N), please use batch mode, ie ``MODEL.LSTR.INFERENCE_MODE batch``'
)
preds_v = (pred_scores_verb[vid][start_step][None, 1:, ::-1]).transpose(0, 2, 1)
preds_n = (pred_scores_noun[vid][start_step][None, 1:, ::-1]).transpose(0, 2, 1)
else:
preds_v = None
preds_n = None
if start_step >= cfg.MODEL.LSTR.ANTICIPATION_LENGTH:
predictions.append(preds)
if preds_v is not None:
predictions_verb.append(preds_v)
if preds_n is not None:
predictions_noun.append(preds_n)
labels.append(segment[1][['verb', 'noun', 'action']].values.astype(int))
ids.append(segment[1]['id'])
else:
discarded_labels.append(segment[1][['verb', 'noun', 'action']].values.astype(int))
discarded_ids.append(segment[1]['id'])
actions = pd.read_csv(osp.join(path_to_data, 'actions.csv'), index_col='id')
action_scores = np.concatenate(predictions)
if len(predictions_verb) > 0:
verb_scores = np.concatenate(predictions_verb)
else:
verb_scores = None
if len(predictions_noun) > 0:
noun_scores = np.concatenate(predictions_noun)
else:
noun_scores = None
labels = np.array(labels)
ids = np.array(ids)
vi = get_marginal_indexes(actions, 'verb')
ni = get_marginal_indexes(actions, 'noun')
action_probs = softmax(action_scores.reshape(-1, action_scores.shape[-1]))
if verb_scores is None:
verb_scores = marginalize(action_probs, vi).reshape(action_scores.shape[0],
action_scores.shape[1],
-1)
else:
verb_scores = softmax(verb_scores.reshape(-1, verb_scores.shape[-1]))
verb_scores = verb_scores.reshape(action_scores.shape[0],
action_scores.shape[1],
-1)
if noun_scores is None:
noun_scores = marginalize(action_probs, ni).reshape(action_scores.shape[0],
action_scores.shape[1],
-1)
else:
noun_scores = softmax(noun_scores.reshape(-1, noun_scores.shape[-1]))
noun_scores = noun_scores.reshape(action_scores.shape[0],
action_scores.shape[1],
-1)
include_discarded = (cfg.DATA.DATA_NAME == 'EK100')
if include_discarded:
dlab = np.array(discarded_labels)
dislab = np.array(discarded_ids)
ids = np.concatenate([ids, dislab])
num_disc = len(dlab)
labels = np.concatenate([labels, dlab])
verb_scores = np.concatenate((verb_scores, np.zeros((num_disc, *verb_scores.shape[1:]))))
noun_scores = np.concatenate((noun_scores, np.zeros((num_disc, *noun_scores.shape[1:]))))
action_scores = np.concatenate((action_scores, np.zeros((num_disc, *action_scores.shape[1:]))))
verb_labels, noun_labels, action_labels = labels[:, 0], labels[:, 1], labels[:, 2]
verb_accuracies = topk_accuracy_multiple_timesteps(verb_scores, verb_labels)
noun_accuracies = topk_accuracy_multiple_timesteps(noun_scores, noun_labels)
action_accuracies = topk_accuracy_multiple_timesteps(action_scores, action_labels)
if cfg.DATA.DATA_NAME == 'EK55':
many_shot_verbs = pd.read_csv(osp.join(path_to_data, 'EPIC_many_shot_verbs.csv'))['verb_class'].values
many_shot_nouns = pd.read_csv(osp.join(path_to_data, 'EPIC_many_shot_nouns.csv'))['noun_class'].values
actions = pd.read_csv(osp.join(path_to_data, 'actions.csv'))
a_to_vn = {a[1]['id']: tuple(a[1][['verb', 'noun']].values)
for a in actions.iterrows()}
many_shot_actions = [a for a, (v, n) in a_to_vn.items()
if v in many_shot_verbs or n in many_shot_nouns]
verb_recalls = topk_recall_multiple_timesteps(verb_scores, verb_labels,
k=5, classes=many_shot_verbs)
noun_recalls = topk_recall_multiple_timesteps(noun_scores, noun_labels,
k=5, classes=many_shot_nouns)
action_recalls = topk_recall_multiple_timesteps(action_scores, action_labels,
k=5, classes=many_shot_actions)
all_accuracies = np.concatenate([verb_accuracies, noun_accuracies, action_accuracies,
verb_recalls, noun_recalls, action_recalls])
all_accuracies = all_accuracies[[0, 1, 6, 2, 3, 7, 4, 5, 8]]
indices = [
('Verb', 'Top-1 Accuracy'), ('Verb', 'Top-5 Accuracy'),
('Verb', 'Mean Top-5 Recall'),
('Noun', 'Top-1 Accuracy'), ('Noun', 'Top-5 Accuracy'),
('Noun', 'Mean Top-5 Recall'),
('Action', 'Top-1 Accuracy'), ('Action', 'Top-5 Accuracy'),
('Action', 'Mean Top-5 Recall'),
]
else:
overall_verb_recalls = topk_recall_multiple_timesteps(verb_scores, verb_labels, k=5)
overall_noun_recalls = topk_recall_multiple_timesteps(noun_scores, noun_labels, k=5)
overall_action_recalls = topk_recall_multiple_timesteps(action_scores, action_labels, k=5)
unseen = pd.read_csv(osp.join(path_to_data, 'validation_unseen_participants_ids.csv'), names=['id'], squeeze=True)
tail_verbs = pd.read_csv(osp.join(path_to_data, 'validation_tail_verbs_ids.csv'), names=['id'], squeeze=True)
tail_nouns = pd.read_csv(osp.join(path_to_data, 'validation_tail_nouns_ids.csv'), names=['id'], squeeze=True)
tail_actions = pd.read_csv(osp.join(path_to_data, 'validation_tail_actions_ids.csv'), names=['id'], squeeze=True)
unseen_bool_idx = pd.Series(ids).isin(unseen).values
tail_verbs_bool_idx = pd.Series(ids).isin(tail_verbs).values
tail_nouns_bool_idx = pd.Series(ids).isin(tail_nouns).values
tail_actions_bool_idx = pd.Series(ids).isin(tail_actions).values
tail_verb_recalls = topk_recall_multiple_timesteps(
verb_scores[tail_verbs_bool_idx], verb_labels[tail_verbs_bool_idx], k=5)
tail_noun_recalls = topk_recall_multiple_timesteps(
noun_scores[tail_nouns_bool_idx], noun_labels[tail_nouns_bool_idx], k=5)
tail_action_recalls = topk_recall_multiple_timesteps(
action_scores[tail_actions_bool_idx], action_labels[tail_actions_bool_idx], k=5)
unseen_verb_recalls = topk_recall_multiple_timesteps(
verb_scores[unseen_bool_idx], verb_labels[unseen_bool_idx], k=5)
unseen_noun_recalls = topk_recall_multiple_timesteps(
noun_scores[unseen_bool_idx], noun_labels[unseen_bool_idx], k=5)
unseen_action_recalls = topk_recall_multiple_timesteps(
action_scores[unseen_bool_idx], action_labels[unseen_bool_idx], k=5)
all_accuracies = np.concatenate(
[overall_verb_recalls, overall_noun_recalls, overall_action_recalls,
unseen_verb_recalls, unseen_noun_recalls, unseen_action_recalls,
tail_verb_recalls, tail_noun_recalls, tail_action_recalls]
) #9 x 8
indices = [
('Overall Mean Top-5 Recall', 'Verb'), ('Overall Mean Top-5 Recall', 'Noun'),
('Overall Mean Top-5 Recall', 'Action'),
('Unseen Mean Top-5 Recall', 'Verb'), ('Unseen Mean Top-5 Recall', 'Noun'),
('Unseen Mean Top-5 Recall', 'Action'),
('Tail Mean Top-5 Recall', 'Verb'), ('Tail Mean Top-5 Recall', 'Noun'),
('Tail Mean Top-5 Recall', 'Action'),
]
cc = np.linspace(1 / cfg.DATA.FPS * cfg.MODEL.LSTR.ANTICIPATION_LENGTH,
1 / cfg.DATA.FPS * cfg.MODEL.LSTR.ANTICIPATION_SAMPLE_RATE,
cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES, dtype=str)
scores = pd.DataFrame(all_accuracies * 100, columns=cc,
index=pd.MultiIndex.from_tuples(indices))
logger.info(scores)
tta_verb = tta(verb_scores, verb_labels)
tta_noun = tta(noun_scores, noun_labels)
tta_action = tta(action_scores, action_labels)
logger.info(f'\nMean TtA(5): VERB {tta_verb:0.2f} '
f'NOUN: {tta_noun:0.2f} ACTION: {tta_action:0.2f}')

View File

@@ -0,0 +1,26 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
from ..base_trainers.perframe_det_trainer import do_perframe_det_train
from ..engines import TRAINERS as registry
@registry.register('LSTR')
def do_lstr_train(cfg,
data_loaders,
model,
criterion,
optimizer,
scheduler,
device,
checkpointer,
logger):
do_perframe_det_train(cfg,
data_loaders,
model,
criterion,
optimizer,
scheduler,
device,
checkpointer,
logger)

View File

@@ -0,0 +1,4 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
from .evalution import compute_result

View File

@@ -0,0 +1,42 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
from rekognition_online_action_detection.utils.registry import Registry
from .metrics import perframe_average_precision, perstage_average_precision
from .postprocessing import postprocessing as default_pp
compute_result = Registry()
@compute_result.register('perframe')
def eval_perframe(cfg, ground_truth, prediction, **kwargs):
class_names = kwargs.get('class_names', cfg.DATA.CLASS_NAMES)
ignore_index = kwargs.get('ignore_index', cfg.DATA.IGNORE_INDEX)
metrics = kwargs.get('metrics', cfg.DATA.METRICS)
postprocessing = kwargs.get('postprocessing', default_pp(cfg.DATA.DATA_NAME))
return perframe_average_precision(
ground_truth=ground_truth,
prediction=prediction,
class_names=class_names,
ignore_index=ignore_index,
metrics=metrics,
postprocessing=postprocessing,
)
@compute_result.register('perstage')
def eval_perstage(cfg, ground_truth, prediction, **kwargs):
class_names = kwargs.get('class_names', cfg.DATA.CLASS_NAMES)
ignore_index = kwargs.get('ignore_index', cfg.DATA.IGNORE_INDEX)
metrics = kwargs.get('metrics', cfg.DATA.METRICS)
postprocessing = kwargs.get('postprocessing', default_pp(cfg.DATA.DATA_NAME))
return perstage_average_precision(
ground_truth=ground_truth,
prediction=prediction,
class_names=class_names,
ignore_index=ignore_index,
metrics=metrics,
postprocessing=postprocessing,
)

View File

@@ -0,0 +1,138 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
from collections import OrderedDict
from multiprocessing import Pool
import numpy as np
from sklearn.metrics import average_precision_score
def calibrated_average_precision_score(y_true, y_score):
"""Compute calibrated average precision (cAP), which is particularly
proposed for the TVSeries dataset.
"""
y_true_sorted = y_true[np.argsort(-y_score)]
tp = y_true_sorted.astype(float)
fp = np.abs(y_true_sorted.astype(float) - 1)
tps = np.cumsum(tp)
fps = np.cumsum(fp)
ratio = np.sum(tp == 0) / np.sum(tp)
cprec = tps / (tps + fps / (ratio + np.finfo(float).eps) + np.finfo(float).eps)
cap = np.sum(cprec[tp == 1]) / np.sum(tp)
return cap
def perframe_average_precision(ground_truth,
prediction,
class_names,
ignore_index,
metrics,
postprocessing):
"""Compute (frame-level) average precision between ground truth and
predictions data frames.
"""
result = OrderedDict()
ground_truth = np.array(ground_truth)
prediction = np.array(prediction)
# Postprocessing
if postprocessing is not None:
ground_truth, prediction = postprocessing(ground_truth, prediction)
# Build metrics
if metrics == 'AP':
compute_score = average_precision_score
elif metrics == 'cAP':
compute_score = calibrated_average_precision_score
else:
raise RuntimeError('Unknown metrics: {}'.format(metrics))
# set ignore classes including background
ignore_set = set([ignore_index])
# enable background according to ignore_index
# ignore_set = set([ignore_index])
# Compute average precision
result['per_class_AP'] = OrderedDict()
# when training, class_names becomes 2!
for idx, class_name in enumerate(class_names):
if idx not in ignore_set:
if np.any(ground_truth[:, idx]):
result['per_class_AP'][class_name] = compute_score(
ground_truth[:, idx], prediction[:, idx])
result['mean_AP'] = np.mean(list(result['per_class_AP'].values()))
return result
def get_stage_pred_scores(gt_targets, pred_scores, perc_s, perc_e):
starts = []
ends = []
stage_gt_targets = []
stage_pred_scores = []
for i in range(len(gt_targets)):
if gt_targets[i] == 0:
stage_gt_targets.append(gt_targets[i])
stage_pred_scores.append(pred_scores[i])
else:
if i == 0 or gt_targets[i - 1] == 0:
starts.append(i)
if i == len(gt_targets) - 1 or gt_targets[i + 1] == 0:
ends.append(i)
if len(starts) != len(ends):
raise ValueError('starts and ends cannot pair!')
action_lens = [ends[i] - starts[i] for i in range(len(starts))]
stage_starts = [starts[i] + int(action_lens[i] * perc_s) for i in range(len(starts))]
stage_ends = [max(stage_starts[i] + 1, starts[i] + int(action_lens[i] * perc_e)) for i in range(len(starts))]
for i in range(len(starts)):
stage_gt_targets.extend(gt_targets[stage_starts[i]: stage_ends[i]])
stage_pred_scores.extend(pred_scores[stage_starts[i]: stage_ends[i]])
return np.array(stage_gt_targets), np.array(stage_pred_scores)
def perstage_average_precision(ground_truth,
prediction,
class_names,
ignore_index,
metrics,
postprocessing):
result = OrderedDict()
ground_truth = np.array(ground_truth)
prediction = np.array(prediction)
# Postprocessing
if postprocessing is not None:
ground_truth, prediction = postprocessing(ground_truth, prediction)
# Build metrics
if metrics == 'AP':
compute_score = average_precision_score
elif metrics == 'cAP':
compute_score = calibrated_average_precision_score
else:
raise RuntimeError('Unknown metrics: {}'.format(metrics))
# Ignore backgroud class
ignore_set = set([0, ignore_index])
# Compute average precision
for perc_s in range(10):
perc_e = perc_s + 1
stage_name = '{:2}%_{:3}%'.format(perc_s * 10, perc_e * 10)
result[stage_name] = OrderedDict({'per_class_AP': OrderedDict()})
for idx, class_name in enumerate(class_names):
if idx not in ignore_set:
stage_gt_targets, stage_pred_scores = get_stage_pred_scores(
(ground_truth[:, idx] == 1).astype(int),
prediction[:, idx],
perc_s / 10,
perc_e / 10,
)
result[stage_name]['per_class_AP'][class_name] = \
compute_score(stage_gt_targets, stage_pred_scores)
result[stage_name]['mean_AP'] = \
np.mean(list(result[stage_name]['per_class_AP'].values()))
return result

View File

@@ -0,0 +1,36 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import numpy as np
def postprocessing(data_name):
def thumos_postprocessing(ground_truth, prediction, smooth=False, switch=False):
"""
We follow (Shou et al., 2017) and adopt their perframe postprocessing method on THUMOS'14 datset.
Source: https://bitbucket.org/columbiadvmm/cdc/src/master/THUMOS14/eval/PreFrameLabeling/compute_framelevel_mAP.m
"""
# Simple temporal smoothing via NMS of 5-frames window
if smooth:
prob = np.copy(prediction)
prob1 = prob.reshape(1, prob.shape[0], prob.shape[1])
prob2 = np.append(prob[0, :].reshape(1, -1), prob[0: -1, :], axis=0).reshape(1, prob.shape[0], prob.shape[1])
prob3 = np.append(prob[1:, :], prob[-1, :].reshape(1, -1), axis=0).reshape(1, prob.shape[0], prob.shape[1])
prob4 = np.append(prob[0: 2, :], prob[0: -2, :], axis=0).reshape(1, prob.shape[0], prob.shape[1])
prob5 = np.append(prob[2:, :], prob[-2:, :], axis=0).reshape(1, prob.shape[0], prob.shape[1])
probsmooth = np.squeeze(np.max(np.concatenate((prob1, prob2, prob3, prob4, prob5), axis=0), axis=0))
prediction = np.copy(probsmooth)
# Assign cliff diving (5) as diving (8)
if switch:
switch_index = np.where(prediction[:, 5] > prediction[:, 8])[0]
prediction[switch_index, 8] = prediction[switch_index, 5]
# Remove ambiguous (21)
valid_index = np.where(ground_truth[:, 21] != 1)[0]
return ground_truth[valid_index], prediction[valid_index]
return {'THUMOS': thumos_postprocessing}.get(data_name, None)

View File

@@ -0,0 +1,5 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
from .lstr import *
from .models import build_model

View File

@@ -0,0 +1,95 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
__all__ = ['build_feature_head']
import torch
import torch.nn as nn
from rekognition_online_action_detection.config.feature_sizes import \
FEATURE_SIZES
from rekognition_online_action_detection.utils.registry import Registry
FEATURE_HEADS = Registry()
@FEATURE_HEADS.register('EasyCom')
@FEATURE_HEADS.register('EGO4D')
@FEATURE_HEADS.register('EGO4DVAL')
@FEATURE_HEADS.register('THUMOS')
@FEATURE_HEADS.register('TVSeries')
@FEATURE_HEADS.register('EK55')
@FEATURE_HEADS.register('EK100')
@FEATURE_HEADS.register('YTConv')
class BaseFeatureHead(nn.Module):
def __init__(self, cfg):
super(BaseFeatureHead, self).__init__()
if cfg.INPUT.MODALITY in ['visual', 'motion', 'object',
'visual+motion', 'motion+object', 'visual+object',
'visual+motion+object']:
self.with_visual = 'visual' in cfg.INPUT.MODALITY
self.with_motion = 'motion' in cfg.INPUT.MODALITY
self.with_object = 'object' in cfg.INPUT.MODALITY
else:
raise RuntimeError('Unknown modality of {}'.format(cfg.INPUT.MODALITY))
INPUT_VISUAL_FEATURE = cfg.INPUT.VISUAL_FEATURE
INPUT_MOTION_FEATURE = cfg.INPUT.MOTION_FEATURE
INPUT_OBJECT_FEATURE = cfg.INPUT.OBJECT_FEATURE
if self.with_visual and self.with_motion and self.with_object:
visual_size = FEATURE_SIZES[INPUT_VISUAL_FEATURE]
motion_size = FEATURE_SIZES[INPUT_MOTION_FEATURE]
object_size = FEATURE_SIZES[INPUT_OBJECT_FEATURE]
fusion_size = visual_size + motion_size + object_size
elif self.with_visual and self.with_motion:
visual_size = FEATURE_SIZES[INPUT_VISUAL_FEATURE]
motion_size = FEATURE_SIZES[INPUT_MOTION_FEATURE]
fusion_size = visual_size + motion_size
elif self.with_visual:
fusion_size = FEATURE_SIZES[INPUT_VISUAL_FEATURE]
elif self.with_motion:
fusion_size = FEATURE_SIZES[INPUT_MOTION_FEATURE]
elif self.with_object:
fusion_size = FEATURE_SIZES[INPUT_OBJECT_FEATURE]
self.fusion_size = fusion_size
self.d_model = fusion_size
if cfg.MODEL.FEATURE_HEAD.LINEAR_ENABLED:
if cfg.MODEL.FEATURE_HEAD.LINEAR_OUT_FEATURES != -1:
self.d_model = cfg.MODEL.FEATURE_HEAD.LINEAR_OUT_FEATURES
self.input_linear = nn.Sequential(
nn.Linear(fusion_size, self.d_model),
nn.ReLU(inplace=True),
)
else:
self.input_linear = nn.Identity()
if cfg.MODEL.FEATURE_HEAD.DROPOUT > 0:
self.dropout = nn.Dropout(cfg.MODEL.FEATURE_HEAD.DROPOUT)
else:
self.dropout = None
def forward(self, visual_input, motion_input, object_input):
if self.with_visual and self.with_motion and self.with_object:
fusion_input = torch.cat((visual_input, motion_input,
object_input), dim=-1)
elif self.with_visual and self.with_motion:
fusion_input = torch.cat((visual_input, motion_input), dim=-1)
elif self.with_visual:
fusion_input = visual_input
elif self.with_motion:
fusion_input = motion_input
elif self.with_object:
fusion_input = object_input
if self.dropout is not None:
fusion_input = self.dropout(fusion_input)
fusion_input = self.input_linear(fusion_input)
return fusion_input
def build_feature_head(cfg):
data_names = ['YTConv', 'EGO4D']
data_name = next((name for name in data_names if name in cfg.DATA.DATA_NAME), cfg.DATA.DATA_NAME)
feature_head = FEATURE_HEADS[data_name]
return feature_head(cfg)

View File

@@ -0,0 +1,424 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import torch
import torch.nn as nn
import torch.nn.functional as F
from ..utils.ek_utils import action_to_noun_map, action_to_verb_map
from . import transformer as tr
from .feature_head import build_feature_head
from .models import META_ARCHITECTURES as registry
from .normalized_linear import NormalizedLinear
class LSTR(nn.Module):
def __init__(self, cfg):
super(LSTR, self).__init__()
# Build long feature heads
self.long_memory_num_samples = cfg.MODEL.LSTR.LONG_MEMORY_NUM_SAMPLES
self.long_enabled = self.long_memory_num_samples > 0
if self.long_enabled:
self.feature_head_long = build_feature_head(cfg)
# Build work feature head
self.work_memory_num_samples = cfg.MODEL.LSTR.WORK_MEMORY_NUM_SAMPLES
self.work_enabled = self.work_memory_num_samples > 0
if self.work_enabled:
self.feature_head_work = build_feature_head(cfg)
self.d_model = self.feature_head_work.d_model
self.num_heads = cfg.MODEL.LSTR.NUM_HEADS
self.dim_feedforward = cfg.MODEL.LSTR.DIM_FEEDFORWARD
self.dropout = cfg.MODEL.LSTR.DROPOUT
self.activation = cfg.MODEL.LSTR.ACTIVATION
self.num_classes = cfg.DATA.NUM_CLASSES
self.use_v_n = cfg.MODEL.LSTR.V_N_CLASSIFIER
self.encoder_attention_type = cfg.MODEL.LSTR.ENC_ATTENTION_TYPE
self.decay_alpha = cfg.MODEL.LSTR.ENC_ATTENTION_DECAY
self.anticipation_num_samples = cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES
# Build position encoding
self.pos_encoding = tr.PositionalEncoding(self.d_model, self.dropout)
self.long_memory_use_pe = cfg.MODEL.LSTR.LONG_MEMORY_USE_PE
self.work_memory_use_pe = cfg.MODEL.LSTR.WORK_MEMORY_USE_PE
self.include_work = cfg.MODEL.LSTR.LONG_MEMORY_INCLUDE_WORK
self.include_work2 = cfg.MODEL.LSTR.LONG_MEMORY_INCLUDE_WORK2
if self.include_work and self.include_work2:
raise ValueError('cfg.MODEL.LSTR.LONG_MEMORY_INCLUDE_WORK and'
'cfg.MODEL.LSTR.LONG_MEMORY_INCLUDE_WORK2 should'
'not be both True')
# Build LSTR encoder
if self.long_enabled:
self.enc_queries = nn.ModuleList()
self.enc_modules = nn.ModuleList()
for i, param in enumerate(cfg.MODEL.LSTR.ENC_MODULE):
if param[0] != -1:
self.enc_queries.append(nn.Embedding(param[0], self.d_model))
# in TeSTra, not like LSTR, gets atteyntion_type, decay_alpha as parameter
# however, the parameter does not change anything from LSTR
enc_layer = tr.TransformerDecoderLayer(
self.d_model, self.num_heads, self.dim_feedforward,
self.dropout, self.activation,
attention_type=self.encoder_attention_type if i == 0 else 'dotproduct',
decay_alpha=self.decay_alpha if i == 0 else 1.0)
self.enc_modules.append(tr.TransformerDecoder(
enc_layer, param[1], tr.layer_norm(self.d_model, param[2])))
else:
self.enc_queries.append(None)
enc_layer = tr.TransformerEncoderLayer(
self.d_model, self.num_heads, self.dim_feedforward,
self.dropout, self.activation,
attention_type=self.encoder_attention_type)
self.enc_modules.append(tr.TransformerEncoder(
enc_layer, param[1], tr.layer_norm(self.d_model, param[2])))
else:
self.register_parameter('enc_queries', None)
self.register_parameter('enc_modules', None)
# Build LSTR decoder
if self.long_enabled:
param = cfg.MODEL.LSTR.DEC_MODULE
dec_layer = tr.TransformerDecoderLayer(
self.d_model, self.num_heads, self.dim_feedforward,
self.dropout, self.activation)
self.dec_modules = tr.TransformerDecoder(
dec_layer, param[1], tr.layer_norm(self.d_model, param[2]))
else:
param = cfg.MODEL.LSTR.DEC_MODULE
dec_layer = tr.TransformerEncoderLayer(
self.d_model, self.num_heads, self.dim_feedforward,
self.dropout, self.activation)
self.dec_modules = tr.TransformerEncoder(
dec_layer, param[1], tr.layer_norm(self.d_model, param[2]))
# Build decode token (for anticipation)
if self.anticipation_num_samples > 0:
self.dec_query = nn.Embedding(self.anticipation_num_samples, self.d_model)
else:
self.register_parameter('dec_query', None)
# Build classifier
if cfg.MODEL.LSTR.DROPOUT_CLS > 0:
self.dropout_cls = nn.Dropout(cfg.MODEL.LSTR.DROPOUT_CLS)
if self.use_v_n:
self.dropout_cls_v = nn.Dropout(cfg.MODEL.LSTR.DROPOUT_CLS)
self.dropout_cls_n = nn.Dropout(cfg.MODEL.LSTR.DROPOUT_CLS)
else:
self.dropout_cls = None
if cfg.MODEL.LSTR.FC_NORM:
self.classifier = NormalizedLinear(self.d_model, self.num_classes)
else:
self.classifier = nn.Linear(self.d_model, self.num_classes)
if self.use_v_n:
a_to_v = action_to_verb_map(cfg.DATA.EK_EXT_PATH,
action_offset=True,
verb_offset=True)
a_to_n = action_to_noun_map(cfg.DATA.EK_EXT_PATH,
action_offset=True,
noun_offset=True)
num_verbs = max(set(a_to_v.values())) + 1
num_nouns = max(set(a_to_n.values())) + 1
print('Number of verbs (inc. 0):', num_verbs)
print('Number of nouns (inc. 0):', num_nouns)
self.classifier_verb = nn.Linear(self.d_model, num_verbs)
self.classifier_noun = nn.Linear(self.d_model, num_nouns)
self.pred_future = 'PRED_FUTURE' in list(zip(*cfg.MODEL.CRITERIONS))[0]
def forward(self, visual_inputs, motion_inputs, object_inputs, memory_key_padding_mask=None):
if self.long_enabled:
# Compute long memories
if self.long_memory_use_pe:
long_memories = self.pos_encoding(self.feature_head_long(
visual_inputs[:, :self.long_memory_num_samples if not self.include_work2 else None],
motion_inputs[:, :self.long_memory_num_samples if not self.include_work2 else None],
object_inputs[:, :self.long_memory_num_samples if not self.include_work2 else None],
).transpose(0, 1))
long_memories = long_memories.transpose(0, 1) ## transpose back
else:
long_memories = self.feature_head_long(
visual_inputs[:, :self.long_memory_num_samples if not self.include_work2 else None],
motion_inputs[:, :self.long_memory_num_samples if not self.include_work2 else None],
object_inputs[:, :self.long_memory_num_samples if not self.include_work2 else None],
)
batch_size = long_memories.shape[0]
# if long_memories.ndim == 5:
# long_memories = self.pos_encoding_3d_spatial(long_memories)
long_memories = long_memories.view(batch_size, -1, self.d_model).transpose(0, 1)
# print(long_memories.shape) # T(*H*W), B, C
if len(self.enc_modules) > 0:
enc_queries = [
enc_query.weight.unsqueeze(1).repeat(1, long_memories.shape[1], 1)
if enc_query is not None else None
for enc_query in self.enc_queries
]
if self.include_work2:
memory_key_padding_mask = F.pad(memory_key_padding_mask,
(0, self.work_memory_num_samples),
'constant', 0)
# Encode long memories
if enc_queries[0] is not None:
long_memories = self.enc_modules[0](enc_queries[0], long_memories,
memory_key_padding_mask=memory_key_padding_mask)
else:
long_memories = self.enc_modules[0](long_memories)
for enc_query, enc_module in zip(enc_queries[1:], self.enc_modules[1:]):
if enc_query is not None:
long_memories = enc_module(enc_query, long_memories)
else:
long_memories = enc_module(long_memories)
# Concatenate memories
if self.long_enabled:
memory = long_memories
if self.work_enabled:
# Compute work memories
if visual_inputs.ndim == 5:
visual_inputs_avg = visual_inputs[:, self.long_memory_num_samples:].mean((2, 3))
motion_inputs_avg = motion_inputs[:, self.long_memory_num_samples:].mean((2, 3))
object_inputs_avg = object_inputs[:, self.long_memory_num_samples:].mean((2, 3))
else:
visual_inputs_avg = visual_inputs[:, self.long_memory_num_samples:]
motion_inputs_avg = motion_inputs[:, self.long_memory_num_samples:]
object_inputs_avg = object_inputs[:, self.long_memory_num_samples:]
work_memories_no_pe = self.feature_head_work(
visual_inputs_avg,
motion_inputs_avg,
object_inputs_avg
# object_inputs[:, self.long_memory_num_samples:],
).transpose(0, 1)
if self.work_memory_use_pe:
work_memories = self.pos_encoding(work_memories_no_pe,
padding=self.long_memory_num_samples if self.long_memory_use_pe else 0)
else:
work_memories = work_memories_no_pe
if self.dec_query is not None:
anticipate_memories = self.pos_encoding(
self.dec_query.weight.unsqueeze(1).repeat(1, work_memories.shape[1], 1),
padding=(self.long_memory_num_samples + self.work_memory_num_samples
if self.long_memory_use_pe else self.work_memory_num_samples))
work_memories = torch.cat((work_memories, anticipate_memories), dim=0)
# work_memories = self.feature_head_work(
# visual_inputs[:, self.long_memory_num_samples:],
# motion_inputs[:, self.long_memory_num_samples:],
# ).transpose(0, 1)
# Build mask
mask = tr.generate_square_subsequent_mask(
work_memories.shape[0])
mask = mask.to(work_memories.device)
# Compute output
if self.long_enabled:
if self.include_work:
extended_memory = torch.cat((memory, work_memories), dim=0)
extended_mask = F.pad(mask,
(memory.shape[0], 0),
'constant', 0)
extended_mask = extended_mask.to(extended_memory.device)
output = self.dec_modules(
work_memories,
memory=extended_memory,
tgt_mask=mask,
memory_mask=extended_mask,
)
else:
output = self.dec_modules(
work_memories,
memory=memory,
tgt_mask=mask,
)
else:
output = self.dec_modules(
work_memories,
src_mask=mask,
)
full_orig_feats = self.feature_head_work(
visual_inputs_avg,
motion_inputs_avg,
object_inputs_avg
# object_inputs[:, self.long_memory_num_samples:]
).transpose(0, 1)
all_outputs_decoded = output
# Compute classification score
if self.dropout_cls is not None:
output_a = self.dropout_cls(output)
else:
output_a = output
# output_a
# [workmem_len + ant_len, batch_size, self.d_model]: [50, 16, 1024]
# score (before transpose) [len, batch_size, num_classes]
# self.classifier: nn.Linear(self.d_model, self.num_classes)
score = self.classifier(output_a)
score = score.transpose(0, 1)
if self.use_v_n:
output_v = self.dropout_cls_v(output)
output_n = self.dropout_cls_n(output)
score_verb = self.classifier_verb(output_v).transpose(0, 1)
score_noun = self.classifier_noun(output_n).transpose(0, 1)
score = (score, score_verb, score_noun)
if self.pred_future:
return (score,
all_outputs_decoded[1: self.work_memory_num_samples + 1, ...],
full_orig_feats)
else:
return score
@registry.register('LSTR')
class LSTRStream(LSTR):
def __init__(self, cfg):
super(LSTRStream, self).__init__(cfg)
############################
# Cache for stream inference
############################
self.long_memories_cache = None
self.compressed_long_memories_cache = None
def stream_inference(self,
long_visual_inputs,
long_motion_inputs,
long_object_inputs,
work_visual_inputs,
work_motion_inputs,
work_object_inputs,
memory_key_padding_mask=None,
cache_num=1,
cache_id=0):
assert self.long_enabled, 'Long-term memory cannot be empty for stream inference'
assert len(self.enc_modules) > 0, 'LSTR encoder cannot be disabled for stream inference'
if ((long_visual_inputs is not None) and
(long_motion_inputs is not None) and
(long_object_inputs is not None)):
# Compute long memories
long_memories = self.feature_head_long(
long_visual_inputs if not self.include_work2 else torch.cat((long_visual_inputs, work_visual_inputs), dim=1),
long_motion_inputs if not self.include_work2 else torch.cat((long_motion_inputs, work_motion_inputs), dim=1),
long_object_inputs if not self.include_work2 else torch.cat((long_object_inputs, work_object_inputs), dim=1),
).transpose(0, 1)
if self.long_memories_cache is None:
self.long_memories_cache = [long_memories for _ in range(cache_num)]
else:
self.long_memories_cache[cache_id] = torch.cat((
self.long_memories_cache[cache_id][1:], long_memories
))
long_memories = self.long_memories_cache[cache_id]
pos = self.pos_encoding.pe[:self.long_memory_num_samples, :]
enc_queries = [
enc_query.weight.unsqueeze(1).repeat(1, long_memories.shape[1], 1)
if enc_query is not None else None
for enc_query in self.enc_queries
]
if self.include_work2:
memory_key_padding_mask = F.pad(memory_key_padding_mask,
(0, self.work_memory_num_samples),
'constant', 0)
# Encode long memories
long_memories = self.enc_modules[0].stream_inference(enc_queries[0], long_memories, pos,
memory_key_padding_mask=memory_key_padding_mask,
cache_num=cache_num, cache_id=cache_id)
self.compressed_long_memories_cache = long_memories
for enc_query, enc_module in zip(enc_queries[1:], self.enc_modules[1:]):
if enc_query is not None:
long_memories = enc_module(enc_query, long_memories)
else:
long_memories = enc_module(long_memories)
else:
long_memories = self.compressed_long_memories_cache
enc_queries = [
enc_query.weight.unsqueeze(1).repeat(1, long_memories.shape[1], 1)
if enc_query is not None else None
for enc_query in self.enc_queries
]
# Encode long memories
for enc_query, enc_module in zip(enc_queries[1:], self.enc_modules[1:]):
if enc_query is not None:
long_memories = enc_module(enc_query, long_memories)
else:
long_memories = enc_module(long_memories)
# Concatenate memories
if self.long_enabled:
memory = long_memories
if self.work_enabled:
# Compute work memories
work_memories = self.pos_encoding(self.feature_head_work(
work_visual_inputs,
work_motion_inputs,
work_object_inputs,
).transpose(0, 1),
padding=self.long_memory_num_samples if self.long_memory_use_pe else 0)
if self.dec_query is not None:
anticipate_memories = self.pos_encoding(
self.dec_query.weight.unsqueeze(1).repeat(1, work_memories.shape[1], 1),
padding=(self.long_memory_num_samples + self.work_memory_num_samples
if self.long_memory_use_pe else self.work_memory_num_samples))
work_memories = torch.cat((work_memories, anticipate_memories))
# Build mask
mask = tr.generate_square_subsequent_mask(
work_memories.shape[0])
mask = mask.to(work_memories.device)
# Compute output
if self.long_enabled:
if self.include_work:
extended_memory = torch.cat((memory, work_memories), dim=0)
extended_mask = F.pad(mask,
(memory.shape[0], 0),
'constant', 0)
extended_mask = extended_mask.to(extended_memory.device)
output = self.dec_modules(
work_memories,
memory=extended_memory,
tgt_mask=mask,
memory_mask=extended_mask,
)
else:
output = self.dec_modules(
work_memories,
memory=memory,
tgt_mask=mask,
)
else:
output = self.dec_modules(
work_memories,
src_mask=mask,
)
# Compute classification score
score = self.classifier(output)
import pdb; pdb.set_trace()
return score.transpose(0, 1)
def clear_cache(self):
self.long_memories_cache = None
self.compressed_long_memories_cache = None
self.enc_modules[0].clear_cache()

View File

@@ -0,0 +1,15 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
__all__ = ['build_model']
from rekognition_online_action_detection.utils.registry import Registry
META_ARCHITECTURES = Registry()
def build_model(cfg, device=None):
model = META_ARCHITECTURES[cfg.MODEL.MODEL_NAME](cfg)
from .weights_init import weights_init
model.apply(weights_init)
return model.to(device)

View File

@@ -0,0 +1,57 @@
import math
import torch
import torch.nn as nn
from torch import Tensor
from torch.nn.parameter import Parameter
import torch.nn.functional as F
class NormalizedLinear(nn.Module):
r"""Normalized linear layer which was proposed by Jiaqi Wang et. al. in
their technical report for LVIS workshop at ECCV 2020.
https://arxiv.org/pdf/2008.10032.pdf
Args:
in_features: size of each input sample
out_features: size of each output sample
bias: If set to ``False``, the layer will not learn an additive bias.
Default: ``True``
tau: a temperature factor, set as 20 by default by following the paper.
"""
__constants__ = ['in_features', 'out_features']
in_features: int
out_features: int
weight: Tensor
def __init__(self, in_features: int, out_features: int, bias: bool = True, tau: float = 20) -> None:
super().__init__()
self.in_features = in_features
self.out_features = out_features
self.weight = Parameter(torch.Tensor(out_features, in_features))
if bias:
self.bias = Parameter(torch.Tensor(out_features))
else:
self.register_parameter('bias', None)
self.reset_parameters()
self.tau = tau
def reset_parameters(self) -> None:
nn.init.kaiming_uniform_(self.weight, a=math.sqrt(5))
if self.bias is not None:
fan_in, _ = nn.init._calculate_fan_in_and_fan_out(self.weight)
bound = 1 / math.sqrt(fan_in)
nn.init.uniform_(self.bias, -bound, bound)
def forward(self, input: Tensor) -> Tensor:
return F.linear(
input / torch.norm(input, dim=-1, keepdim=True),
self.tau * self.weight /
torch.norm(self.weight, dim=-1, keepdim=True),
self.bias
)
def extra_repr(self) -> str:
return 'in_features={}, out_features={}, bias={}'.format(
self.in_features, self.out_features, self.bias is not None
)

View File

@@ -0,0 +1,10 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
from .position_encoding import PositionalEncoding
from .position_encoding import PositionalEncoding2D
from .transformer import MultiheadAttention
from .transformer import Transformer
from .transformer import TransformerEncoder, TransformerEncoderLayer
from .transformer import TransformerDecoder, TransformerDecoderLayer
from .utils import layer_norm, generate_square_subsequent_mask

View File

@@ -0,0 +1,456 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import torch
from torch._C import device
import torch.nn as nn
import torch.nn.functional as F
import time
class DotProductAttention(nn.Module):
def __init__(self, dropout=0.0, alpha=1.0, num_heads=8):
super(DotProductAttention, self).__init__()
self.dropout = dropout
self.alpha = alpha
self.num_heads = num_heads
self.decay_mat = None
def forward(self, q, k, v, attn_mask=None):
attn_output_weights = torch.bmm(q, k.transpose(1, 2))
if attn_mask is not None:
attn_output_weights += attn_mask
# attn_output_weights = F.softmax(attn_output_weights, dim=-1)
# attn_output_weights = F.dropout(attn_output_weights,
# p=self.dropout,
# training=self.training)
if self.alpha != 1.0 and self.decay_mat is None:
self.decay_mat = torch.vander(torch.tensor([self.alpha] * self.num_heads), N=v.shape[1])
self.decay_mat = self.decay_mat.to(v.device)
if self.alpha != 1.0:
bs = attn_output_weights.shape[0] // self.num_heads
decay_mat = self.decay_mat.unsqueeze(0).repeat(bs, 1, 1)
decay_mat = decay_mat.view(-1, *decay_mat.shape[2:])
attn_output_weights = torch.log(decay_mat[:, None, :] + 1e-10) + attn_output_weights
attn_output_weights = F.softmax(attn_output_weights, dim=-1)
attn_output_weights = F.dropout(attn_output_weights,
p=self.dropout,
training=self.training)
attn_output = torch.bmm(attn_output_weights, v)
return attn_output
class DotProductAttentionStream(DotProductAttention):
def __init__(self, dropout=0.0, alpha=1.0):
super(DotProductAttentionStream, self).__init__(dropout, alpha)
############################
# Cache for stream inference
############################
self.k_weights_cache = None
self.k_pos_weights_cache = None
self.QK_exp_cache = None
self.recache_cnt = 0
self.recache_freq = 99999
self.alpha = alpha
def clear_cache(self):
self.k_weights_cache = None
self.k_pos_weights_cache = None
self.QK_exp_cache = None
self.recache_cnt = 0
def stream_inference(self, q, k, v, k_pos, v_pos, attn_mask=None, cache_num=1, cache_id=0):
'''
if self.k_weights_cache is not None:
k_weights_new = torch.bmm(q, k[:, [-1]].transpose(1, 2))
k_weights = torch.cat((self.k_weights_cache[:, :, 1:], k_weights_new), dim=-1)
self.k_weights_cache = k_weights
# k_pos_weights = self.k_pos_weights_cache
else:
k_weights = torch.bmm(q, k.transpose(1, 2))
self.k_weights_cache = k_weights
# k_pos_weights = torch.bmm(q, k_pos.transpose(1, 2))
# self.k_pos_weights_cache = k_pos_weights
# attn_output_weights = k_weights + k_pos_weights
attn_output_weights = k_weights
if attn_mask is not None:
attn_output_weights += attn_mask
attn_output_weights = F.softmax(attn_output_weights, dim=-1)
attn_output_weights = F.dropout(attn_output_weights,
p=self.dropout,
training=self.training)
# attn_output = torch.bmm(attn_output_weights, (v + v_pos))
attn_output = torch.bmm(attn_output_weights, v)
return attn_output
'''
B = 1
num = 1
q = q.repeat(B, 1, 1)
k = k.repeat(B, num, 1)
v = v.repeat(B, num, 1)
attn_mask = attn_mask.repeat(B, 1, num)
if self.alpha <= 1.0:
if self.QK_exp_cache is None:
QK = torch.bmm(q, k.transpose(1, 2)) # q~(H, Tq, C), k~(H, Tk, C) => O(H*Tq*Tk*C)
self.QK_max = QK.max(dim=-1, keepdim=True).values
QK_exp = torch.exp(QK - self.QK_max + attn_mask) # => O(H*Tq*Tk)
self.QK_exp_cache = [QK_exp for _ in range(cache_num)]
self.QKV_exp_cache = [torch.bmm(QK_exp, v) for _ in range(cache_num)] # => O(H*Tq*Tk*C)
self.Z = [QK_exp.sum(dim=-1, keepdim=True) for _ in range(cache_num)] # => O(H*Tq*Tk)
output = self.QKV_exp_cache[cache_id] / self.Z[cache_id]
self.recache_cnt += 1
return output
else:
QK_push = torch.bmm(q, k[:, -num:, :].transpose(1, 2)) # => O(H*Tq*C)
QK_exp_push = torch.exp(QK_push - self.QK_max) # => O(H*Tq*Tk)
sum_push = QK_exp_push.sum(-1, keepdim=True)
QKV_exp_push = torch.bmm(QK_exp_push, v[:, -num:, :]) # => O(H*Tq*Tk)
self.QKV_exp_cache[cache_id] = self.alpha * self.QKV_exp_cache[cache_id] + QKV_exp_push # => O(H*Tq*Tk)
self.Z[cache_id] = self.alpha * self.Z[cache_id] + sum_push
output = self.QKV_exp_cache[cache_id] / self.Z[cache_id]
self.recache_cnt += 1
return output
if self.QK_exp_cache is None:
QK = torch.bmm(q, k.transpose(1, 2)) # q~(H, Tq, C), k~(H, Tk, C) => O(H*Tq*Tk*C)
self.QK_max = QK.max(dim=-1, keepdim=True).values
QK_exp = torch.exp(QK - self.QK_max + attn_mask) # => O(H*Tq*Tk)
self.QK_exp_cache = [QK_exp for _ in range(cache_num)]
self.QKV_exp_cache = [torch.bmm(QK_exp, v) for _ in range(cache_num)] # => O(H*Tq*Tk*C)
self.Z = [QK_exp.sum(dim=-1, keepdim=True) for _ in range(cache_num)] # => O(H*Tq*Tk)
self.QKV_exp_pop = [torch.bmm(self.QK_exp_cache[cache_i][:, :, :num], v[:, :num, :]) for cache_i in range(cache_num)]
self.sum_pop = [self.QK_exp_cache[cache_i][:, :, :num].sum(-1, keepdim=True) for cache_i in range(cache_num)]
output = self.QKV_exp_cache[cache_id] / self.Z[cache_id]
self.recache_cnt += 1
return output
elif (self.recache_cnt // cache_num) % self.recache_freq == 0:
QK = torch.bmm(q, k.transpose(1, 2)) # q~(H, Tq, C), k~(H, Tk, C) => O(H*Tq*Tk*C)
self.QK_max = QK.max(dim=-1, keepdim=True).values
QK_exp = torch.exp(QK - self.QK_max + attn_mask) # => O(H*Tq*Tk)
self.QK_exp_cache[cache_id] = QK_exp
self.QKV_exp_cache[cache_id] = torch.bmm(QK_exp, v) # => O(H*Tq*Tk*C)
self.Z[cache_id] = QK_exp.sum(dim=-1, keepdim=True) # => O(H*Tq*Tk)
self.QKV_exp_pop[cache_id] = torch.bmm(self.QK_exp_cache[cache_id][:, :, :num], v[:, :num, :])
self.sum_pop[cache_id] = self.QK_exp_cache[cache_id][:, :, :num].sum(-1, keepdim=True)
output = self.QKV_exp_cache[cache_id] / self.Z[cache_id]
self.recache_cnt += 1
return output
else:
QK_push = torch.bmm(q, k[:, -num:, :].transpose(1, 2)) # => O(H*Tq*C)
QK_exp_push = torch.exp(QK_push - self.QK_max) # => O(H*Tq*Tk)
sum_push = QK_exp_push.sum(-1, keepdim=True)
QKV_exp_push = torch.bmm(QK_exp_push, v[:, -num:, :]) # => O(H*Tq*Tk)
# add with no decay
self.QKV_exp_cache[cache_id] = self.QKV_exp_cache[cache_id] + QKV_exp_push # => O(H*Tq*Tk)
# add with no decay
self.Z[cache_id] = self.Z[cache_id] + sum_push # => O(H*Tq*Tk)
self.QK_exp_cache[cache_id] = torch.cat((self.QK_exp_cache[cache_id][:, :, num:], QK_exp_push), dim=-1)
self.QKV_exp_pop[cache_id] = torch.bmm(self.QK_exp_cache[cache_id][:, :, :num], v[:, :num, :])
self.sum_pop[cache_id] = self.QK_exp_cache[cache_id][:, :, :num].sum(-1, keepdim=True)
output = self.QKV_exp_cache[cache_id] / self.Z[cache_id]
self.recache_cnt += 1
return output
class MultiheadAttention(nn.Module):
def __init__(self, embed_dim, num_heads, dropout=0.0, bias=True, kdim=None, vdim=None,
attention_type='dotproduct', decay_alpha=1.0):
super(MultiheadAttention, self).__init__()
self.embed_dim = embed_dim
self.num_heads = num_heads
self.kdim = kdim if kdim is not None else embed_dim
self.vdim = vdim if vdim is not None else embed_dim
self._qkv_same_embed_dim = self.kdim == embed_dim and self.vdim == embed_dim
if self._qkv_same_embed_dim:
self.in_proj_weight = nn.Parameter(torch.empty(3 * embed_dim, embed_dim))
else:
raise RuntimeError('Do not support q, k, v have different dimensions')
if bias:
self.in_proj_bias = nn.Parameter(torch.empty(3 * embed_dim))
else:
self.register_parameter('in_proj_bias', None)
self.out_proj = nn.Linear(embed_dim, embed_dim)
if self._qkv_same_embed_dim:
nn.init.xavier_uniform_(self.in_proj_weight)
if self.in_proj_bias is not None:
nn.init.constant_(self.in_proj_bias, 0.)
nn.init.constant_(self.out_proj.bias, 0.)
if attention_type == 'dotproduct':
self.attention = DotProductAttention(dropout, decay_alpha, num_heads)
elif attention_type == 'linear':
self.attention = LinearAttention(dropout)
else:
raise RuntimeError('attention_type should be [dotproduct | linear]')
def forward(self, q, k, v, attn_mask=None, key_padding_mask=None):
tsz, bsz, embed_dim = q.shape[0], q.shape[1], q.shape[2]
head_dim = embed_dim // self.num_heads
assert head_dim * self.num_heads == embed_dim, \
'embed_dim must be divisible by num_heads'
scaling = float(head_dim) ** -0.5
_b = self.in_proj_bias
_start = None
_end = embed_dim
_w = self.in_proj_weight[:_end, :]
if _b is not None:
_b = _b[:_end]
q = F.linear(q, _w, _b)
_b = self.in_proj_bias
_start = embed_dim
_end = embed_dim * 2
_w = self.in_proj_weight[_start:_end, :]
if _b is not None:
_b = _b[_start:_end]
k = F.linear(k, _w, _b)
_b = self.in_proj_bias
_start = embed_dim * 2
_end = None
_w = self.in_proj_weight[_start:, :]
if _b is not None:
_b = _b[_start:]
v = F.linear(v, _w, _b)
q = q * scaling
q = q.contiguous().view(-1, bsz * self.num_heads, head_dim).transpose(0, 1)
k = k.contiguous().view(-1, bsz * self.num_heads, head_dim).transpose(0, 1)
v = v.contiguous().view(-1, bsz * self.num_heads, head_dim).transpose(0, 1)
if attn_mask is not None:
attn_mask = attn_mask.unsqueeze(0).repeat(bsz, 1, 1)
attn_mask = attn_mask.unsqueeze(1).repeat(1, self.num_heads, 1, 1)
attn_mask = attn_mask.reshape(-1, *attn_mask.shape[2:])
if key_padding_mask is not None:
if key_padding_mask.ndim == 4:
key_padding_mask = key_padding_mask.unsqueeze(1).repeat(1, tsz, 1, 1, 1)
key_padding_mask = key_padding_mask.unsqueeze(1).repeat(1, self.num_heads, 1, 1, 1, 1)
key_padding_mask = key_padding_mask.reshape(*key_padding_mask.shape[:3], -1)
else:
key_padding_mask = key_padding_mask.unsqueeze(1).repeat(1, tsz, 1)
key_padding_mask = key_padding_mask.unsqueeze(1).repeat(1, self.num_heads, 1, 1)
key_padding_mask = key_padding_mask.reshape(-1, *key_padding_mask.shape[2:])
if attn_mask is not None and key_padding_mask is not None:
mask = attn_mask + key_padding_mask
elif attn_mask is not None:
mask = attn_mask
elif key_padding_mask is not None:
mask = key_padding_mask
else:
mask = None
attn_output = self.attention(q, k, v, mask)
attn_output = attn_output.transpose(0, 1).contiguous().view(tsz, bsz,
self.embed_dim)
return self.out_proj(attn_output), None
class MultiheadAttentionStream(MultiheadAttention):
def __init__(self, embed_dim, num_heads, dropout=0.0, bias=True, kdim=None, vdim=None,
attention_type='dotproduct', decay_alpha=1.0):
super(MultiheadAttentionStream, self).__init__(embed_dim, num_heads,
dropout, bias, kdim,
vdim, attention_type,
decay_alpha)
if attention_type == 'dotproduct':
self.attention = DotProductAttentionStream(dropout, decay_alpha)
elif attention_type == 'linear':
self.attention = LinearAttentionStream(dropout)
else:
raise RuntimeError('attention_type should be [dotproduct | linear]')
############################
# Cache for stream inference
############################
self.q_cache = None
self.k_cache = None
self.v_cache = None
self.k_pos_cache = None
self.v_pos_cache = None
def clear_cache(self):
self.q_cache = None
self.k_cache = None
self.v_cache = None
self.k_pos_cache = None
self.v_pos_cache = None
self.attention.clear_cache()
def stream_inference(self, q, k, v, pos, attn_mask=None, key_padding_mask=None,
cache_num=1, cache_id=0):
tsz, bsz, embed_dim = q.shape[0], q.shape[1], q.shape[2]
head_dim = embed_dim // self.num_heads
assert head_dim * self.num_heads == embed_dim, \
'embed_dim must be divisible by num_heads'
scaling = float(head_dim) ** -0.5
if self.q_cache is not None:
q = self.q_cache
else:
_b = self.in_proj_bias
_start = None
_end = embed_dim
_w = self.in_proj_weight[:_end, :]
if _b is not None:
_b = _b[:_end]
q = F.linear(q, _w, _b)
self.q_cache = q
assert (self.k_cache is None) == (self.k_pos_cache is None)
if self.k_cache is not None:
_b = self.in_proj_bias
_start = embed_dim
_end = embed_dim * 2
_w = self.in_proj_weight[_start:_end, :]
if _b is not None:
_b = _b[_start:_end]
k_new = F.linear(k[[-1]], _w, None)
k = torch.cat((self.k_cache[1:], k_new))
self.k_cache = k
k_pos = self.k_pos_cache
else:
_b = self.in_proj_bias
_start = embed_dim
_end = embed_dim * 2
_w = self.in_proj_weight[_start:_end, :]
if _b is not None:
_b = _b[_start:_end]
k = F.linear(k, _w, None)
self.k_cache = k
k_pos = F.linear(pos, _w, _b)
self.k_pos_cache = k_pos
assert (self.v_cache is None) == (self.v_pos_cache is None)
if self.v_cache is not None:
_b = self.in_proj_bias
_start = embed_dim * 2
_end = None
_w = self.in_proj_weight[_start:, :]
if _b is not None:
_b = _b[_start:]
v_new = F.linear(v[[-1]], _w, None)
v = torch.cat((self.v_cache[1:], v_new))
self.v_cache = v
v_pos = self.v_pos_cache
else:
_b = self.in_proj_bias
_start = embed_dim * 2
_end = None
_w = self.in_proj_weight[_start:, :]
if _b is not None:
_b = _b[_start:]
v = F.linear(v, _w, None)
self.v_cache = v
v_pos = F.linear(pos, _w, _b)
self.v_pos_cache = v_pos
q = q * scaling
q = q.contiguous().view(-1, bsz * self.num_heads, head_dim).transpose(0, 1)
k = k.contiguous().view(-1, bsz * self.num_heads, head_dim).transpose(0, 1)
v = v.contiguous().view(-1, bsz * self.num_heads, head_dim).transpose(0, 1)
k_pos = k_pos.contiguous().view(-1, bsz * self.num_heads, head_dim).transpose(0, 1)
v_pos = v_pos.contiguous().view(-1, bsz * self.num_heads, head_dim).transpose(0, 1)
if attn_mask is not None:
attn_mask = attn_mask.unsqueeze(0).repeat(bsz, 1, 1)
attn_mask = attn_mask.unsqueeze(1).repeat(1, self.num_heads, 1, 1)
attn_mask = attn_mask.reshape(-1, *attn_mask.shape[2:])
if key_padding_mask is not None:
key_padding_mask = key_padding_mask.unsqueeze(1).repeat(1, tsz, 1)
key_padding_mask = key_padding_mask.unsqueeze(1).repeat(1, self.num_heads, 1, 1)
key_padding_mask = key_padding_mask.reshape(-1, *key_padding_mask.shape[2:])
if attn_mask is not None and key_padding_mask is not None:
mask = attn_mask + key_padding_mask
elif attn_mask is not None:
mask = attn_mask
elif key_padding_mask is not None:
mask = key_padding_mask
else:
mask = None
attn_output = self.attention.stream_inference(q, k, v, k_pos, v_pos, mask,
cache_num=cache_num, cache_id=cache_id)
attn_output = attn_output.transpose(0, 1).contiguous().view(tsz, bsz,
self.embed_dim)
return self.out_proj(attn_output), None
class LinearAttention(nn.Module):
def __init__(self, dropout=0.0, nonlinear=F.elu,
eps=1e-6):
super(LinearAttention, self).__init__()
self.dropout = dropout
self.nonlinear = nonlinear
self.eps = eps
def forward(self, q, k, v, attn_mask=None):
kk = self.nonlinear(k) + 1
qq = self.nonlinear(q) + 1
vv = self.nonlinear(v) + 1
kv = torch.bmm(kk.transpose(1, 2), vv)
ks = torch.sum(kk, dim=1, keepdim=True) # (B 1 C)
Z = 1 / (torch.bmm(qq, ks.transpose(1, 2)) + self.eps)
attn_output = torch.bmm(qq, kv.transpose(1, 2)) * Z
# if attn_mask is not None:
# print(attn_mask.shape)
# raise RuntimeError(("LinearAttention does not support arbitrary "
# "attention masks"))
return attn_output
class LinearAttentionStream(LinearAttention):
def __init__(self, dropout=0.0):
super(LinearAttentionStream, self).__init__(dropout)
############################
# Cache for stream inference
############################
self.k_weights_cache = None
self.k_pos_weights_cache = None
def stream_inference(self, q, k, v, k_pos, v_pos, attn_mask=None):
raise NotImplementedError

View File

@@ -0,0 +1,53 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import math
import torch
import torch.nn as nn
class PositionalEncoding(nn.Module):
def __init__(self, d_model, dropout=0.1, max_len=5000):
super(PositionalEncoding, self).__init__()
self.dropout = nn.Dropout(p=dropout)
pe = torch.zeros(max_len, d_model)
position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
pe[:, 0::2] = torch.sin(position * div_term)
pe[:, 1::2] = torch.cos(position * div_term)
pe = pe.unsqueeze(0).transpose(0, 1)
self.register_buffer('pe', pe)
def forward(self, x, padding=0):
x = x + self.pe[padding: padding + x.shape[0], :]
return self.dropout(x)
class PositionalEncoding2D(nn.Module):
def __init__(self, d_model, dropout=0.1, height=64, width=64):
super(PositionalEncoding2D, self).__init__()
self.dropout = nn.Dropout(p=dropout)
pe = torch.zeros(height, width, d_model)
position_x = torch.arange(0, width, dtype=torch.float).unsqueeze(1)
position_y = torch.arange(0, height, dtype=torch.float).unsqueeze(1)
d_model = int(d_model / 2)
div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
pe[:, :, 0:d_model:2] = torch.sin(position_x * div_term).unsqueeze(0).repeat(height, 1, 1)
pe[:, :, 1:d_model:2] = torch.cos(position_x * div_term).unsqueeze(0).repeat(height, 1, 1)
pe[:, :, d_model::2] = torch.sin(position_y * div_term).unsqueeze(1).repeat(1, width, 1)
pe[:, :, d_model + 1::2] = torch.cos(position_y * div_term).unsqueeze(1).repeat(1, width, 1)
pe = pe.unsqueeze(0).unsqueeze(0) #.transpose(0, 1)
self.register_buffer('pe', pe)
def forward(self, x, padding=(0,0)):
x = x + self.pe[:, :,
padding[0]: padding[0] + x.shape[2],
padding[1]: padding[1] + x.shape[3], :]
return self.dropout(x)

View File

@@ -0,0 +1,250 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import copy
import torch
import torch.nn as nn
import torch.nn.functional as F
from .multihead_attention import MultiheadAttentionStream as MultiheadAttention
class Transformer(nn.Module):
def __init__(self, d_model=512, nhead=8, num_encoder_layers=6,
num_decoder_layers=6, dim_feedforward=2048, dropout=0.1,
activation='relu', custom_encoder=None, custom_decoder=None):
super(Transformer, self).__init__()
if custom_encoder is not None:
self.encoder = custom_encoder
else:
encoder_layer = TransformerEncoderLayer(d_model, nhead, dim_feedforward, dropout, activation)
encoder_norm = nn.LayerNorm(d_model)
self.encoder = TransformerEncoder(encoder_layer, num_encoder_layers, encoder_norm)
if custom_decoder is not None:
self.decoder = custom_decoder
else:
decoder_layer = TransformerDecoderLayer(d_model, nhead, dim_feedforward, dropout, activation)
decoder_norm = nn.LayerNorm(d_model)
self.decoder = TransformerDecoder(decoder_layer, num_decoder_layers, decoder_norm)
self.d_model = d_model
self.nhead = nhead
def forward(self, src, tgt, src_mask=None, tgt_mask=None,
memory_mask=None, src_key_padding_mask=None,
tgt_key_padding_mask=None, memory_key_padding_mask=None):
if src.size(1) != tgt.size(1):
raise RuntimeError('the batch number of src and tgt must be equal')
if src.size(2) != self.d_model or tgt.size(2) != self.d_model:
raise RuntimeError('the feature number of src and tgt must be equal to d_model')
memory = self.encoder(src, mask=src_mask, src_key_padding_mask=src_key_padding_mask)
output = self.decoder(tgt, memory, tgt_mask=tgt_mask, memory_mask=memory_mask,
tgt_key_padding_mask=tgt_key_padding_mask,
memory_key_padding_mask=memory_key_padding_mask)
return output
class TransformerEncoder(nn.Module):
def __init__(self, encoder_layer, num_layers, norm=None):
super(TransformerEncoder, self).__init__()
self.layers = _get_clones(encoder_layer, num_layers)
self.num_layers = num_layers
self.norm = norm
def forward(self, src, src_mask=None, src_key_padding_mask=None):
output = src
for mod in self.layers:
output = mod(output, src_mask=src_mask,
src_key_padding_mask=src_key_padding_mask)
if self.norm is not None:
output = self.norm(output)
return output
class TransformerDecoder(nn.Module):
def __init__(self, decoder_layer, num_layers, norm=None):
super(TransformerDecoder, self).__init__()
self.layers = _get_clones(decoder_layer, num_layers)
self.num_layers = num_layers
self.norm = norm
def clear_cache(self):
if len(self.layers) != 1:
raise RuntimeError('Number of layers cannot larger than 1 for stream inference')
self.layers[0].clear_cache()
def stream_inference(self, tgt, memory, pos, tgt_mask=None,
memory_mask=None, tgt_key_padding_mask=None,
memory_key_padding_mask=None, cache_num=1, cache_id=0):
output = tgt
if len(self.layers) != 1:
raise RuntimeError('Number of layers cannot larger than 1 for stream inference')
output = self.layers[0].stream_inference(output, memory, pos,
tgt_mask=tgt_mask,
memory_mask=memory_mask,
tgt_key_padding_mask=tgt_key_padding_mask,
memory_key_padding_mask=memory_key_padding_mask,
cache_num=cache_num, cache_id=cache_id)
if self.norm is not None:
output = self.norm(output)
return output
def forward(self, tgt, memory, tgt_mask=None,
memory_mask=None, tgt_key_padding_mask=None,
memory_key_padding_mask=None):
output = tgt
for mod in self.layers:
output = mod(output, memory, tgt_mask=tgt_mask,
memory_mask=memory_mask,
tgt_key_padding_mask=tgt_key_padding_mask,
memory_key_padding_mask=memory_key_padding_mask)
if self.norm is not None:
output = self.norm(output)
return output
class TransformerEncoderLayer(nn.Module):
def __init__(self, d_model, nhead, dim_feedforward=2048, dropout=0.1, activation='relu'):
super(TransformerEncoderLayer, self).__init__()
self.self_attn = MultiheadAttention(d_model, nhead, dropout=dropout)
# Implementation of Feedforward model
self.linear1 = nn.Linear(d_model, dim_feedforward)
self.dropout = nn.Dropout(dropout)
self.linear2 = nn.Linear(dim_feedforward, d_model)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.dropout1 = nn.Dropout(dropout)
self.dropout2 = nn.Dropout(dropout)
self.activation = _get_activation_fn(activation)
def __setstate__(self, state):
if 'activation' not in state:
state['activation'] = F.relu
super(TransformerEncoderLayer, self).__setstate__(state)
def forward(self, src, src_mask=None, src_key_padding_mask=None):
src2 = self.self_attn(src, src, src, attn_mask=src_mask,
key_padding_mask=src_key_padding_mask)[0]
src = src + self.dropout1(src2)
src = self.norm1(src)
src2 = self.linear2(self.dropout(self.activation(self.linear1(src))))
src = src + self.dropout2(src2)
src = self.norm2(src)
return src
class TransformerDecoderLayer(nn.Module):
def __init__(self, d_model, nhead, dim_feedforward=2048, dropout=0.1,
activation='relu', attention_type='dotproduct',
decay_alpha=1.0):
super(TransformerDecoderLayer, self).__init__()
self.self_attn = MultiheadAttention(d_model, nhead, dropout=dropout,
attention_type=attention_type)
self.multihead_attn = MultiheadAttention(d_model, nhead, dropout=dropout,
attention_type=attention_type,
decay_alpha=decay_alpha)
# Implementation of Feedforward model
self.linear1 = nn.Linear(d_model, dim_feedforward)
self.dropout = nn.Dropout(dropout)
self.linear2 = nn.Linear(dim_feedforward, d_model)
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.norm3 = nn.LayerNorm(d_model)
self.dropout1 = nn.Dropout(dropout)
self.dropout2 = nn.Dropout(dropout)
self.dropout3 = nn.Dropout(dropout)
self.activation = _get_activation_fn(activation)
############################
# Cache for stream inference
############################
self.tgt_cache = None
def __setstate__(self, state):
if 'activation' not in state:
state['activation'] = F.relu
super(TransformerDecoderLayer, self).__setstate__(state)
def clear_cache(self):
self.tgt_cache = None
self.multihead_attn.clear_cache()
def stream_inference(self, tgt, memory, pos, tgt_mask=None, memory_mask=None,
tgt_key_padding_mask=None, memory_key_padding_mask=None,
cache_num=1, cache_id=0):
if self.tgt_cache is None:
tgt2 = self.self_attn(tgt, tgt, tgt, attn_mask=tgt_mask,
key_padding_mask=tgt_key_padding_mask)[0]
tgt = tgt + self.dropout1(tgt2)
tgt = self.norm1(tgt)
self.tgt_cache = tgt
else:
tgt = self.tgt_cache
tgt2 = self.multihead_attn.stream_inference(tgt, memory, memory, pos, attn_mask=memory_mask,
key_padding_mask=memory_key_padding_mask,
cache_num=cache_num, cache_id=cache_id)[0]
tgt = tgt + self.dropout2(tgt2)
tgt = self.norm2(tgt)
tgt2 = self.linear2(self.dropout(self.activation(self.linear1(tgt))))
tgt = tgt + self.dropout3(tgt2)
tgt = self.norm3(tgt)
return tgt
def forward(self, tgt, memory, tgt_mask=None, memory_mask=None,
tgt_key_padding_mask=None, memory_key_padding_mask=None):
tgt2 = self.self_attn(tgt, tgt, tgt, attn_mask=tgt_mask,
key_padding_mask=tgt_key_padding_mask)[0]
tgt = tgt + self.dropout1(tgt2)
tgt = self.norm1(tgt)
tgt2 = self.multihead_attn(tgt, memory, memory, attn_mask=memory_mask,
key_padding_mask=memory_key_padding_mask)[0]
tgt = tgt + self.dropout2(tgt2)
tgt = self.norm2(tgt)
tgt2 = self.linear2(self.dropout(self.activation(self.linear1(tgt))))
tgt = tgt + self.dropout3(tgt2)
tgt = self.norm3(tgt)
return tgt
def _get_clones(module, N):
return nn.ModuleList([copy.deepcopy(module) for i in range(N)])
def _get_activation_fn(activation):
if activation == 'relu':
return F.relu
elif activation == 'gelu':
return F.gelu
raise RuntimeError('activation should be relu/gelu, not {}'.format(activation))

View File

@@ -0,0 +1,16 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import torch
assert torch.__version__ >= '1.6.0'
import torch.nn as nn
def layer_norm(d_model, condition=True):
return nn.LayerNorm(d_model) if condition else None
def generate_square_subsequent_mask(sz):
mask = (torch.triu(torch.ones(sz, sz)) == 1).transpose(0, 1)
mask = mask.float().masked_fill(mask == 0, float('-inf')).masked_fill(mask == 1, float(0.0))
return mask

View File

@@ -0,0 +1,33 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import math
import torch.nn as nn
def weights_init(m):
if isinstance(m, nn.Linear):
nn.init.kaiming_uniform_(m.weight.data, a=math.sqrt(5))
elif isinstance(m, nn.Conv1d):
nn.init.normal_(m.weight.data)
if m.bias is not None:
nn.init.normal_(m.bias.data)
elif isinstance(m, nn.Conv2d):
nn.init.xavier_normal_(m.weight.data)
if m.bias is not None:
nn.init.normal_(m.bias.data)
elif isinstance(m, nn.ConvTranspose1d):
nn.init.normal_(m.weight.data)
if m.bias is not None:
nn.init.normal_(m.bias.data)
elif isinstance(m, nn.ConvTranspose2d):
nn.init.xavier_normal_(m.weight.data)
if m.bias is not None:
nn.init.normal_(m.bias.data)
elif isinstance(m, nn.BatchNorm1d):
nn.init.normal_(m.weight.data, mean=1, std=0.02)
nn.init.constant_(m.bias.data, 0)
elif isinstance(m, nn.BatchNorm2d):
nn.init.normal_(m.weight.data, mean=1, std=0.02)
nn.init.constant_(m.bias.data, 0)

View File

@@ -0,0 +1,5 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
from .optimizers import build_optimizer
from .lr_scheduler import build_scheduler

View File

@@ -0,0 +1,224 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import math
from bisect import bisect_left
import numpy as np
import torch
from torch.optim.lr_scheduler import _LRScheduler
def _get_warmup_factor_at_iter(warmup_method,
this_iter,
warmup_iters,
warmup_factor):
if this_iter >= warmup_iters:
return 1.0
if warmup_method == 'constant':
return warmup_factor
elif warmup_method == 'linear':
alpha = this_iter / warmup_iters
return warmup_factor * (1 - alpha) + alpha
else:
raise ValueError('Unknown warmup method: {}'.format(warmup_method))
class MultiStepLR(_LRScheduler):
def __init__(self,
optimizer,
milestones,
gamma=0.1,
last_epoch=-1):
if not list(milestones) == sorted(milestones):
raise ValueError(
'Milestones should be a list of increasing integers. Got {}'.format(milestones)
)
self.milestones = milestones
self.gamma = gamma
super().__init__(optimizer, last_epoch)
def get_lr(self):
return [
base_lr
* self.gamma ** bisect_left(self.milestones, self.last_epoch)
for base_lr in self.base_lrs
]
def _compute_values(self):
# The new interface
return self.get_lr()
class WarmupMultiStepLR(_LRScheduler):
def __init__(self,
optimizer,
milestones,
gamma=0.1,
warmup_factor=0.3,
warmup_iters=500,
warmup_method='linear',
last_epoch=-1):
if not list(milestones) == sorted(milestones):
raise ValueError(
'Milestones should be a list of increasing integers. Got {}'.format(milestones)
)
self.milestones = milestones
self.gamma = gamma
self.warmup_factor = warmup_factor
self.warmup_iters = warmup_iters
self.warmup_method = warmup_method
super().__init__(optimizer, last_epoch)
def get_lr(self):
warmup_factor = _get_warmup_factor_at_iter(
self.warmup_method,
self.last_epoch,
self.warmup_iters,
self.warmup_factor,
)
import pdb; pdb.set_trace()
return [
base_lr
* warmup_factor
* self.gamma ** bisect_left(self.milestones, self.last_epoch)
for base_lr in self.base_lrs
]
def _compute_values(self):
# The new interface
return self.get_lr()
class CosineLR(_LRScheduler):
def __init__(self,
optimizer,
max_iters,
last_epoch=-1):
self.max_iters = max_iters
super().__init__(optimizer, last_epoch)
def get_lr(self):
return [
base_lr
* 0.5
* (1.0 + math.cos(math.pi * self.last_epoch / self.max_iters))
for base_lr in self.base_lrs
]
def _compute_values(self):
# The new interface
return self.get_lr()
class WarmupCosineLR(_LRScheduler):
def __init__(self,
optimizer,
max_iters,
gamma=0.1,
warmup_factor=0.3,
warmup_iters=500,
warmup_method='linear',
last_epoch=-1):
self.max_iters = max_iters
self.warmup_factor = warmup_factor
self.warmup_iters = warmup_iters
self.warmup_method = warmup_method
super().__init__(optimizer, last_epoch)
def get_lr(self):
warmup_factor = _get_warmup_factor_at_iter(
self.warmup_method,
self.last_epoch,
self.warmup_iters,
self.warmup_factor,
)
return [
base_lr
* warmup_factor
* 0.5
* (1.0 + math.cos(math.pi * self.last_epoch / self.max_iters))
for base_lr in self.base_lrs
]
def _compute_values(self):
# The new interface
return self.get_lr()
class DummyScheduler:
def __init__(self, optimizer):
self.optimizer = optimizer
self.base_lrs = [group['lr'] for group in optimizer.param_groups]
def get_last_lr(self):
return self.base_lrs
def step(self):
pass # No operation for stepping the scheduler
def build_scheduler(cfg, optimizer, num_iters_per_epoch):
"""Unlike the PyTorch version, our schedulers adjust the learning rate
according to iteration rather than epoch.
"""
# Set last epoch (here 'epoch' is actually 'iters')
last_epoch = (cfg.SOLVER.START_EPOCH - 1) * num_iters_per_epoch
if cfg.SOLVER.SCHEDULER.SCHEDULER_NAME == 'none' or cfg.SOLVER.SCHEDULER.SCHEDULER_NAME is None:
scheduler = DummyScheduler(optimizer)
elif cfg.SOLVER.SCHEDULER.SCHEDULER_NAME == 'multistep':
# Convert milestones epochs to iters
milestones = [(i - 1) * num_iters_per_epoch for i in cfg.SOLVER.SCHEDULER.MILESTONES]
scheduler = MultiStepLR(
optimizer,
milestones=milestones,
gamma=cfg.SOLVER.SCHEDULER.GAMMA,
last_epoch=last_epoch)
elif cfg.SOLVER.SCHEDULER.SCHEDULER_NAME == 'warmup_multistep':
# Convert milestones epochs to iters
milestones = [(i - 1) * num_iters_per_epoch for i in cfg.SOLVER.SCHEDULER.MILESTONES]
# Convert warmup epochs to iters
cfg.SOLVER.SCHEDULER.WARMUP_ITERS = int(cfg.SOLVER.SCHEDULER.WARMUP_EPOCHS * num_iters_per_epoch)
scheduler = WarmupMultiStepLR(
optimizer,
milestones=milestones,
gamma=cfg.SOLVER.SCHEDULER.GAMMA,
warmup_factor=cfg.SOLVER.SCHEDULER.WARMUP_FACTOR,
warmup_iters=cfg.SOLVER.SCHEDULER.WARMUP_ITERS,
warmup_method=cfg.SOLVER.SCHEDULER.WARMUP_METHOD,
last_epoch=last_epoch)
elif cfg.SOLVER.SCHEDULER.SCHEDULER_NAME == 'cosine':
# Get max number of iters
max_iters = cfg.SOLVER.NUM_EPOCHS * num_iters_per_epoch
scheduler = CosineLR(
optimizer,
max_iters=max_iters,
last_epoch=last_epoch)
elif cfg.SOLVER.SCHEDULER.SCHEDULER_NAME == 'warmup_cosine':
# Get max number of iters
max_iters = cfg.SOLVER.NUM_EPOCHS * num_iters_per_epoch
# Convert warmup epochs to iters
cfg.SOLVER.SCHEDULER.WARMUP_ITERS = int(cfg.SOLVER.SCHEDULER.WARMUP_EPOCHS * num_iters_per_epoch)
scheduler = WarmupCosineLR(
optimizer,
max_iters=max_iters,
gamma=cfg.SOLVER.SCHEDULER.GAMMA,
warmup_factor=cfg.SOLVER.SCHEDULER.WARMUP_FACTOR,
warmup_iters=cfg.SOLVER.SCHEDULER.WARMUP_ITERS,
warmup_method=cfg.SOLVER.SCHEDULER.WARMUP_METHOD,
last_epoch=last_epoch)
else:
raise RuntimeError('Unknown lr scheduler: {}'.format(cfg.SOLVER.SCHEDULER.SCHEDULER_NAME))
return scheduler

View File

@@ -0,0 +1,27 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
__all__ = ['build_optimizer']
import torch.optim as optim
def build_optimizer(cfg, model):
if cfg.SOLVER.OPTIMIZER == 'sgd':
optimizer = optim.SGD(
[{'params': model.parameters(), 'initial_lr': cfg.SOLVER.BASE_LR}],
lr=cfg.SOLVER.BASE_LR, weight_decay=cfg.SOLVER.WEIGHT_DECAY, momentum=cfg.SOLVER.MOMENTUM,
)
elif cfg.SOLVER.OPTIMIZER == 'adam':
optimizer = optim.Adam(
[{'params': model.parameters(), 'initial_lr': cfg.SOLVER.BASE_LR}],
lr=cfg.SOLVER.BASE_LR, weight_decay=cfg.SOLVER.WEIGHT_DECAY,
)
elif cfg.SOLVER.OPTIMIZER == 'adamw':
optimizer = optim.AdamW(
[{'params': model.parameters(), 'initial_lr': cfg.SOLVER.BASE_LR}],
lr=cfg.SOLVER.BASE_LR, weight_decay=cfg.SOLVER.WEIGHT_DECAY,
)
else:
raise RuntimeError('Unknown optimizer: {}'.format(cfg.SOLVER.OPTIMIZER))
return optimizer

View File

@@ -0,0 +1,2 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0

View File

@@ -0,0 +1,144 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
__all__ = ['setup_checkpointer']
import os.path as osp
import torch
class Checkpointer(object):
def __init__(self, cfg, phase):
# Load pretrained checkpoint
self.is_pretrained = False
self.cfg = cfg
self.checkpoint = self._load_checkpoint(cfg.MODEL.CHECKPOINT)
self.except_classifier = cfg.MODEL.EXCEPT_CLASSIFIER if phase == 'train' else False
if self.checkpoint is not None and phase == 'train':
# further training with the same dataset: resume
model_ckpt_str = cfg['MODEL']['CHECKPOINT'].replace('_', '')
if cfg['DATA']['DATA_NAME'].lower() in model_ckpt_str.lower():
cfg.SOLVER.START_EPOCH += self.checkpoint.get('epoch', 0)
# further training with the different dataset: finetuning on pretrained weight
else:
self.is_pretrained = True
print(f'WARNING) Finetuning on pretrained weight: {cfg.MODEL.CHECKPOINT}')
elif self.checkpoint is None and phase != 'train':
# no checkpoint in infernece mode: error
raise RuntimeError('Cannot find checkpoint {}'.format(cfg.MODEL.CHECKPOINT))
self.output_dir = cfg.OUTPUT_DIR
def load(self, model, optimizer=None):
if self.checkpoint is not None:
# try load the ckpt first, then if there is a size mismatch, do other operation
if not self.is_pretrained: # just resume
# Attempt to load the state dictionary from the checkpoint
model.load_state_dict(self.checkpoint['model_state_dict'])
# Load the optimizer state if provided
if optimizer is not None:
if 'optimizer_state_dict' in self.checkpoint:
optimizer.load_state_dict(self.checkpoint['optimizer_state_dict'])
else:
print('Optimizer state dictionary not found in checkpoint.')
else:
# different model structure (pretrained)
print(f'WARNING) Load checkpoint separately due to possiblity of different model structure, src/rekognition_online_action_detection/utils/checkpointer.py L51')
model_dict = model.state_dict()
unnecessary_keys = []
unmatched_keys = []
matched_ckpt = {}
# loaded_state_dict = self.checkpoint['model_state_dict']
# # Initialize a flag to track if all values match
# all_values_match = True
# # Iterate through all keys and compare values
# for key in model_dict:
# import pdb; pdb.set_trace()
# if not torch.allclose(model_dict[key], loaded_state_dict[key]):
# print(f"Mismatch found in {key}")
# print(f"Model value: {model_dict[key]}")
# print(f"Loaded value: {loaded_state_dict[key]}")
# print(f"Difference: {model_dict[key] - loaded_state_dict[key]}")
# print("---")
# all_values_match = False
# Filter out unnecessary keys
for k, v in self.checkpoint['model_state_dict'].items():
if k not in model_dict:
print(f'Unnecessary key: {k} not found in model')
unnecessary_keys.append(k)
elif model_dict[k].shape != v.shape:
print(f'Unnecessary key: {k} shape {v.shape} does not match {model_dict[k].shape}')
unmatched_keys.append(k)
elif self.except_classifier is True and 'classifier' in k:
print(f'Unnecessary key: {k} is classifier')
unnecessary_keys.append(k)
else:
matched_ckpt[k] = v
print(f'Unloaded keys: {unnecessary_keys}')
print(f'Unmathced keys: {unmatched_keys}')
# Update the existing state dict with the matched keys
# import pdb; pdb.set_trace()
model_dict.update(matched_ckpt)
model.load_state_dict(model_dict)
# for unmatched_key in unmatched_keys:
# loaded_dict_shape = self.checkpoint['model_state_dict'][unmatched_key].shape
# model_dict_shape = model_dict[unmatched_key].shape
# if len(loaded_dict_shape) == 2:
# if loaded_dict_shape[0] != model_dict_shape[0]:
# model_dict[unmatched_key][:loaded_dict_shape[0], :] = self.checkpoint['model_state_dict'][unmatched_key]
# print(f'Updated key: {unmatched_key}, model_dict[{unmatched_key}][:{loaded_dict_shape[0]}, :] = checkpoint["model_state_dict"][{unmatched_key}]')
# else:
# model_dict[unmatched_key][:, :loaded_dict_shape[1]] = self.checkpoint['model_state_dict'][unmatched_key]
# print(f'Updated key: {unmatched_key}, model_dict[{unmatched_key}][:, :{loaded_dict_shape[1]}] = checkpoint["model_state_dict"][{unmatched_key}]')
# elif len(loaded_dict_shape) == 1:
# model_dict[unmatched_key][:loaded_dict_shape[0]] = self.checkpoint['model_state_dict'][unmatched_key]
# print(f'Updated key: {unmatched_key}, model_dict[{unmatched_key}][:{loaded_dict_shape[0]}] = checkpoint["model_state_dict"][{unmatched_key}]')
# elif len(loaded_dict_shape) >= 3:
# raise ValueError(f'Unsupported shape: {loaded_dict_shape}')
# model.load_state_dict(model_dict)
def save(self, epoch, model, optimizer):
torch.save({
'epoch': epoch,
'model_state_dict': model.module.state_dict() if torch.cuda.device_count() > 1 else model.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
}, osp.join(self.output_dir, f'epoch-{epoch}.pth'))
print(f'\nSave checkpoint to {osp.join(self.output_dir, f"epoch-{epoch}.pth")}')
# osp.join(self.output_dir, f'{self.cfg.INPUT.MODALITY}_epoch-{epoch}.pth'))
def save_best(self, epoch, model, optimizer):
# save_path = osp.join(self.output_dir, f'{self.cfg.INPUT.MODALITY}_best.pth')
save_path = osp.join(self.output_dir, f'best.pth')
torch.save({
'epoch': epoch,
'model_state_dict': model.module.state_dict() if torch.cuda.device_count() > 1 else model.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
}, save_path)
print(f'\nSave checkpoint to {save_path}')
def _load_checkpoint(self, checkpoint):
if checkpoint is not None and osp.isfile(checkpoint):
return torch.load(checkpoint, map_location=torch.device('cpu'))
elif osp.isdir(checkpoint):
import pdb; pdb.set_trace()
return None
def setup_checkpointer(cfg, phase):
return Checkpointer(cfg, phase)

View File

@@ -0,0 +1,20 @@
import os.path as osp
import pandas as pd
def action_to_verb_map(path_to_data,
action_offset=False,
verb_offset=False):
actions = pd.read_csv(osp.join(path_to_data, 'actions.csv'))
a_to_v = {a[1]['id'] + action_offset: a[1]['verb'] + verb_offset
for a in actions.iterrows()}
return a_to_v
def action_to_noun_map(path_to_data,
action_offset=False,
noun_offset=False):
actions = pd.read_csv(osp.join(path_to_data, 'actions.csv'))
a_to_n = {a[1]['id'] + action_offset: a[1]['noun'] + noun_offset
for a in actions.iterrows()}
return a_to_n

View File

@@ -0,0 +1,32 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
__all__ = [
'setup_random_seed',
'setup_environment',
]
import os
import random
import torch
import numpy as np
def setup_random_seed(seed):
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
if torch.cuda.is_available():
torch.cuda.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
torch.backends.cudnn.benchmark = False
torch.backends.cudnn.deterministic = True
def setup_environment(cfg):
os.environ['CUDA_VISIBLE_DEVICES'] = cfg.GPU
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
if cfg.SEED is not None:
setup_random_seed(cfg.SEED)
return device

View File

@@ -0,0 +1,32 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
__all__ = ['setup_logger']
import os
import sys
import logging
import pprint
def setup_logger(cfg, phase, quiet=False):
logger = logging.getLogger('rekognition')
level = logging.DEBUG if cfg.VERBOSE else logging.INFO
logger.setLevel(level)
ch = logging.StreamHandler(stream=sys.stdout)
ch.setLevel(level)
logger.addHandler(ch)
if phase == 'train':
log_file = os.path.join(cfg.OUTPUT_DIR, 'log.txt')
os.makedirs(cfg.OUTPUT_DIR, exist_ok=True)
else:
log_file = os.path.splitext(cfg.MODEL.CHECKPOINT)[0] + '.txt'
fh = logging.FileHandler(log_file)
fh.setLevel(logging.INFO)
logger.addHandler(fh)
if not quiet:
logger.info(pprint.pformat(cfg))
return logger

View File

@@ -0,0 +1,114 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import argparse
import json
import os.path as osp
from rekognition_online_action_detection.config.defaults import get_cfg
def parse_args():
parser = argparse.ArgumentParser(description='Rekognition Face Anti-Spoofing')
parser.add_argument(
'--config_file',
default='',
type=str,
help='path to yaml config file',
)
parser.add_argument(
'--gpu',
default='0',
type=str,
help='specify visible devices'
)
parser.add_argument(
'opts',
default=None,
nargs='*',
help='modify config options using the command-line',
)
return parser.parse_args()
def assert_and_infer_cfg(cfg, args):
# Setup the visible devices
cfg.GPU = args.gpu
# Infer data info
with open(cfg.DATA.DATA_INFO, 'r') as f:
data_info = json.load(f)[cfg.DATA.DATA_NAME]
cfg.DATA.DATA_ROOT = data_info['data_root'] if cfg.DATA.DATA_ROOT is None else cfg.DATA.DATA_ROOT
cfg.DATA.CLASS_NAMES = data_info['class_names'] if cfg.DATA.CLASS_NAMES is None else cfg.DATA.CLASS_NAMES
cfg.DATA.NUM_CLASSES = data_info['num_classes'] if cfg.DATA.NUM_CLASSES is None else cfg.DATA.NUM_CLASSES
cfg.DATA.IGNORE_INDEX = data_info['ignore_index'] if cfg.DATA.IGNORE_INDEX is None else cfg.DATA.IGNORE_INDEX
cfg.DATA.METRICS = data_info['metrics'] if cfg.DATA.METRICS is None else cfg.DATA.METRICS
cfg.DATA.FPS = data_info['fps'] if cfg.DATA.FPS is None else cfg.DATA.FPS
cfg.DATA.TRAIN_SESSION_SET = data_info['train_session_set'] if cfg.DATA.TRAIN_SESSION_SET is None else cfg.DATA.TRAIN_SESSION_SET
cfg.DATA.TEST_SESSION_SET = data_info['test_session_set'] if cfg.DATA.TEST_SESSION_SET is None else cfg.DATA.TEST_SESSION_SET
# Ignore two mis-labeled videos
if False and cfg.DATA_NAME == 'THUMOS':
cfg.DATA.TEST_SESSION_SET.remove('video_test_0000270')
cfg.DATA.TEST_SESSION_SET.remove('video_test_0001496')
# Input assertions
if cfg.INPUT.MODALITY == 'twostream':
cfg.INPUT.MODALITY = 'visual+motion'
if cfg.INPUT.MODALITY == 'threestream':
cfg.INPUT.MODALITY = 'visual+motion+object'
assert cfg.INPUT.MODALITY in ['visual', 'motion', 'object',
'visual+motion', 'motion+object', 'visual+object',
'visual+motion+object']
# Infer memory
if cfg.MODEL.MODEL_NAME in ['LSTR']:
cfg.MODEL.LSTR.AGES_MEMORY_LENGTH = cfg.MODEL.LSTR.AGES_MEMORY_SECONDS * cfg.DATA.FPS
cfg.MODEL.LSTR.LONG_MEMORY_LENGTH = cfg.MODEL.LSTR.LONG_MEMORY_SECONDS * cfg.DATA.FPS
cfg.MODEL.LSTR.WORK_MEMORY_LENGTH = cfg.MODEL.LSTR.WORK_MEMORY_SECONDS * cfg.DATA.FPS
cfg.MODEL.LSTR.TOTAL_MEMORY_LENGTH = \
cfg.MODEL.LSTR.AGES_MEMORY_LENGTH + \
cfg.MODEL.LSTR.LONG_MEMORY_LENGTH + \
cfg.MODEL.LSTR.WORK_MEMORY_LENGTH
cfg.MODEL.LSTR.ANTICIPATION_LENGTH = cfg.MODEL.LSTR.ANTICIPATION_SECONDS * cfg.DATA.FPS
assert cfg.MODEL.LSTR.AGES_MEMORY_LENGTH % cfg.MODEL.LSTR.AGES_MEMORY_SAMPLE_RATE == 0
assert cfg.MODEL.LSTR.LONG_MEMORY_LENGTH % cfg.MODEL.LSTR.LONG_MEMORY_SAMPLE_RATE == 0
assert cfg.MODEL.LSTR.WORK_MEMORY_LENGTH % cfg.MODEL.LSTR.WORK_MEMORY_SAMPLE_RATE == 0
assert cfg.MODEL.LSTR.ANTICIPATION_LENGTH % cfg.MODEL.LSTR.ANTICIPATION_SAMPLE_RATE == 0
cfg.MODEL.LSTR.AGES_MEMORY_NUM_SAMPLES = cfg.MODEL.LSTR.AGES_MEMORY_LENGTH // cfg.MODEL.LSTR.AGES_MEMORY_SAMPLE_RATE
cfg.MODEL.LSTR.LONG_MEMORY_NUM_SAMPLES = cfg.MODEL.LSTR.LONG_MEMORY_LENGTH // cfg.MODEL.LSTR.LONG_MEMORY_SAMPLE_RATE
cfg.MODEL.LSTR.WORK_MEMORY_NUM_SAMPLES = cfg.MODEL.LSTR.WORK_MEMORY_LENGTH // cfg.MODEL.LSTR.WORK_MEMORY_SAMPLE_RATE
cfg.MODEL.LSTR.TOTAL_MEMORY_NUM_SAMPLES = \
cfg.MODEL.LSTR.AGES_MEMORY_NUM_SAMPLES + \
cfg.MODEL.LSTR.LONG_MEMORY_NUM_SAMPLES + \
cfg.MODEL.LSTR.WORK_MEMORY_NUM_SAMPLES
cfg.MODEL.LSTR.ANTICIPATION_NUM_SAMPLES = cfg.MODEL.LSTR.ANTICIPATION_LENGTH // cfg.MODEL.LSTR.ANTICIPATION_SAMPLE_RATE
assert cfg.MODEL.LSTR.INFERENCE_MODE in ['batch', 'stream']
# Infer output dir
config_name = osp.splitext(args.config_file)[0].split('/')[1:]
cfg.OUTPUT_DIR = osp.join(cfg.OUTPUT_DIR, *config_name)
if cfg.SESSION:
cfg.OUTPUT_DIR = osp.join(cfg.OUTPUT_DIR, cfg.SESSION)
def load_cfg():
args = parse_args()
cfg = get_cfg()
for i in range(0, len(args.opts), 2):
key = args.opts[i]
value = args.opts[i+1]
if key == 'MODEL.LSTR.LONG_MEMORY_SECONDS' or key == 'MODEL.LSTR.WORK_MEMORY_SECONDS':
args.opts[i+1] = int(value)
print(f'(key, value): ({key}, {value}) is converted to integer type')
if args.config_file is not None:
cfg.merge_from_file(args.config_file)
if args.opts is not None:
cfg.merge_from_list(args.opts)
assert_and_infer_cfg(cfg, args)
# config_file is in cfg.OUTPUT_DIR or args.config_file
return cfg

View File

@@ -0,0 +1,23 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
def _register_generic(module_dict, module_name, module):
assert module_name not in module_dict
module_dict[module_name] = module
class Registry(dict):
def __init__(self, *args, **kwargs):
super(Registry, self).__init__(*args, **kwargs)
def register(self, module_name, module=None):
if module is not None:
_register_generic(self, module_name, module)
return
def register_fn(fn):
_register_generic(self, module_name, fn)
return fn
return register_fn

View File

@@ -0,0 +1,162 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import logging
logging.basicConfig(level=logging.INFO, format='%(message)s')
import argparse
import numpy as np
import pickle as pkl
import pandas as pd
import os.path as osp
from rekognition_online_action_detection.evaluation import compute_result
from external.rulstm.RULSTM.utils import (get_marginal_indexes, marginalize, softmax,
topk_accuracy_multiple_timesteps,
topk_recall_multiple_timesteps,
tta)
def eval_fusion(pred_scores_files, weights):
### Comments: Per-frame scores cost too much space...
# perframe_gt_targets_fusion = {}
# perframe_pred_scores_fusion = {}
# for weight, pred_scores_file in zip(weights, pred_scores_files):
# pred_scores = pkl.load(open(pred_scores_file, 'rb'))
# cfg = pred_scores['cfg']
# assert cfg.DATA.DATA_NAME in ['EK55', 'EK100']
# perframe_gt_targets = pred_scores['perframe_gt_targets']
# perframe_pred_scores = pred_scores['perframe_pred_scores']
# for session in perframe_pred_scores:
# if session not in perframe_pred_scores_fusion:
# perframe_pred_scores_fusion[session] = weight * perframe_pred_scores[session]
# else:
# perframe_pred_scores_fusion[session] += weight * perframe_pred_scores[session]
# perframe_gt_targets_fusion[session] = perframe_gt_targets[session]
# # Compute results
# result = compute_result['perframe'](
# cfg,
# np.concatenate(list(perframe_gt_targets_fusion.values()), axis=0),
# np.concatenate(list(perframe_pred_scores_fusion.values()), axis=0),
# )
# logging.info('Action detection perframe m{}: {:.5f}'.format(
# cfg.DATA.METRICS, result['mean_AP']
# ))
action_scores = None
for weight, pred_scores_file in zip(weights, pred_scores_files):
pred_scores = pkl.load(open(pred_scores_file, 'rb'))
cfg = pred_scores['cfg']
assert cfg.DATA.DATA_NAME in ['EK55', 'EK100']
if action_scores is None:
action_scores = weight * pred_scores['action_scores']
else:
action_scores += weight * pred_scores['action_scores']
# segment-level evaluation on EK55/EK100
if cfg.DATA.DATA_NAME == 'EK55':
path_to_data = 'external/rulstm/RULSTM/data/ek55/'
segment_list = pd.read_csv(osp.join(path_to_data, 'validation.csv'),
names=['id', 'video', 'start_f', 'end_f', 'verb', 'noun', 'action'],
skipinitialspace=True)
predictions, labels, ids = [], [], []
discarded_labels, discarded_ids = [], []
for idx in segment_list['id']:
segment = segment_list[segment_list['id'] == idx]
start_step = int(np.floor(segment['start_f'].values.astype(int) / 30 * cfg.DATA.FPS))
vid = segment['video'].values[0].strip()
# preds = (perframe_pred_scores_fusion[vid][start_step][None, 1:, ::-1]).transpose(0, 2, 1)
if start_step >= cfg.MODEL.LSTR.ANTICIPATION_LENGTH:
# predictions.append(preds)
labels.append(segment[['verb', 'noun', 'action']].values.astype(int))
ids.append(idx)
else:
discarded_labels.append(segment[['verb', 'noun', 'action']].values.astype(int))
discarded_ids.append(idx)
actions = pd.read_csv(osp.join(path_to_data, 'actions.csv'), index_col='id')
# action_scores = np.concatenate(predictions)
labels = np.concatenate(labels)
ids = np.array(ids)
vi = get_marginal_indexes(actions, 'verb')
ni = get_marginal_indexes(actions, 'noun')
action_probs = softmax(action_scores.reshape(-1, action_scores.shape[-1]))
# action_probs = action_scores.reshape(-1, action_scores.shape[-1])
# action_probs = action_probs / action_probs.sum(-1, keepdims=True)
print(action_probs.shape)
verb_scores = marginalize(action_probs, vi).reshape(action_scores.shape[0],
action_scores.shape[1],
-1)
noun_scores = marginalize(action_probs, ni).reshape(action_scores.shape[0],
action_scores.shape[1],
-1)
include_discarded = (cfg.DATA.DATA_NAME == 'EK100')
if include_discarded:
dlab = np.array(discarded_labels)
dislab = np.array(discarded_ids)
ids = np.concatenate([ids, dislab])
num_disc = len(dlab)
labels = np.concatenate([labels, dlab])
verb_scores = np.concatenate((verb_scores, np.zeros((num_disc, *verb_scores.shape[1:]))))
noun_scores = np.concatenate((noun_scores, np.zeros((num_disc, *noun_scores.shape[1:]))))
action_scores = np.concatenate((action_scores, np.zeros((num_disc, *action_scores.shape[1:]))))
verb_labels, noun_labels, action_labels = labels[:, 0], labels[:, 1], labels[:, 2]
verb_accuracies = topk_accuracy_multiple_timesteps(verb_scores, verb_labels)
noun_accuracies = topk_accuracy_multiple_timesteps(noun_scores, noun_labels)
action_accuracies = topk_accuracy_multiple_timesteps(action_scores, action_labels)
many_shot_verbs = pd.read_csv(osp.join(path_to_data, 'EPIC_many_shot_verbs.csv'))['verb_class'].values
many_shot_nouns = pd.read_csv(osp.join(path_to_data, 'EPIC_many_shot_nouns.csv'))['noun_class'].values
actions = pd.read_csv(osp.join(path_to_data, 'actions.csv'))
a_to_vn = {a[1]['id']: tuple(a[1][['verb', 'noun']].values)
for a in actions.iterrows()}
many_shot_actions = [a for a, (v, n) in a_to_vn.items()
if v in many_shot_verbs or n in many_shot_nouns]
verb_recalls = topk_recall_multiple_timesteps(verb_scores, verb_labels,
k=5, classes=many_shot_verbs)
noun_recalls = topk_recall_multiple_timesteps(noun_scores, noun_labels,
k=5, classes=many_shot_nouns)
action_recalls = topk_recall_multiple_timesteps(action_scores, action_labels,
k=5, classes=many_shot_actions)
all_accuracies = np.concatenate([verb_accuracies, noun_accuracies, action_accuracies,
verb_recalls, noun_recalls, action_recalls])
all_accuracies = all_accuracies[[0, 1, 6, 2, 3, 7, 4, 5, 8]]
indices = [
('Verb', 'Top-1 Accuracy'), ('Verb', 'Top-5 Accuracy'),
('Verb', 'Mean Top-5 Recall'),
('Noun', 'Top-1 Accuracy'), ('Noun', 'Top-5 Accuracy'),
('Noun', 'Mean Top-5 Recall'),
('Action', 'Top-1 Accuracy'), ('Action', 'Top-5 Accuracy'),
('Action', 'Mean Top-5 Recall'),
]
cc = np.linspace(1 / cfg.DATA.FPS * cfg.MODEL.LSTR.ANTICIPATION_LENGTH,
1 / cfg.DATA.FPS, cfg.MODEL.LSTR.ANTICIPATION_LENGTH, dtype=str)
scores = pd.DataFrame(all_accuracies * 100, columns=cc,
index=pd.MultiIndex.from_tuples(indices))
print(scores)
tta_verb = tta(verb_scores, verb_labels)
tta_noun = tta(noun_scores, noun_labels)
tta_action = tta(action_scores, action_labels)
print(f'\nMean TtA(5): VERB {tta_verb:0.2f} '
f'NOUN: {tta_noun:0.2f} ACTION: {tta_action:0.2f}')
else:
raise NotImplementedError
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--pred_scores_files', nargs='+', required=True)
parser.add_argument('--weights', type=float, nargs='+', required=True)
args = parser.parse_args()
assert len(args.pred_scores_files) == len(args.weights)
eval_fusion(args.pred_scores_files, args.weights)

View File

@@ -0,0 +1,36 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import logging
logging.basicConfig(level=logging.INFO, format='%(message)s')
import argparse
import numpy as np
import pickle as pkl
from rekognition_online_action_detection.evaluation import compute_result
def eval_perframe(pred_scores_file):
pred_scores = pkl.load(open(pred_scores_file, 'rb'))
cfg = pred_scores['cfg']
perframe_gt_targets = pred_scores['perframe_gt_targets']
perframe_pred_scores = pred_scores['perframe_pred_scores']
# Compute results
result = compute_result['perframe'](
cfg,
np.concatenate(list(perframe_gt_targets.values()), axis=0),
np.concatenate(list(perframe_pred_scores.values()), axis=0),
)
logging.info('Action detection perframe m{}: {:.5f}'.format(
cfg.DATA.METRICS, result['mean_AP']
))
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--pred_scores_file', type=str, required=True)
args = parser.parse_args()
eval_perframe(args.pred_scores_file)

View File

@@ -0,0 +1,37 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import logging
logging.basicConfig(level=logging.INFO, format='%(message)s')
import argparse
import numpy as np
import pickle as pkl
from rekognition_online_action_detection.evaluation import compute_result
def eval_perstage(pred_scores_file):
pred_scores = pkl.load(open(pred_scores_file, 'rb'))
cfg = pred_scores['cfg']
perframe_gt_targets = pred_scores['perframe_gt_targets']
perframe_pred_scores = pred_scores['perframe_pred_scores']
# Compute results
result = compute_result['perstage'](
cfg,
np.concatenate(list(perframe_gt_targets.values()), axis=0),
np.concatenate(list(perframe_pred_scores.values()), axis=0),
)
for stage_name in result:
logging.info('Perframe m{} of stage {}: {:.5f}'.format(
cfg.DATA.METRICS, stage_name, result[stage_name]['mean_AP']
))
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--pred_scores_file', type=str, required=True)
args = parser.parse_args()
eval_perstage(args.pred_scores_file)

View File

@@ -0,0 +1,80 @@
import argparse
import numpy as np
import pandas as pd
import os.path as osp
def str2sec(string):
hh, mm, ss = string.split(':')
return float(hh) * 3600 + float(mm) * 60 + float(ss)
def main():
parser = argparse.ArgumentParser(description='Generate targets')
parser.add_argument('--dataset', type=str,
choices=['EK55', 'EK100'], default='EK55')
parser.add_argument('--data-path', type=str,
default='/scratch/cluster/yzhao/Datasets/EPIC-KITCHENS/')
parser.add_argument('--targets-path', type=str,
default='/scratch/cluster/yzhao/Datasets/'
'EPIC-KITCHENS/features/target_perframe/')
parser.add_argument('--fps', type=int, default=30)
parser.add_argument('--tick', type=float, default=7.5)
parser.add_argument('--anno-path', type=str,
default='external/rulstm/RULSTM/data/ek55/')
parser.add_argument('--class-type', type=str, default='action',
choices=['verb', 'noun', 'action'])
args = parser.parse_args()
# initialize the targets
targets = {}
if args.class_type == 'action':
num_classes = 2513 + 1
elif args.class_type == 'verb':
actions = pd.read_csv(osp.join(args.anno_path, 'actions.csv'))
a_to_v = {a[1]['id'] + 1: a[1]['verb'] + 1
for a in actions.iterrows()}
num_classes = max(a_to_v.values()) + 1
elif args.class_type == 'noun':
actions = pd.read_csv(osp.join(args.anno_path, 'actions.csv'))
a_to_n = {a[1]['id'] + 1: a[1]['noun'] + 1
for a in actions.iterrows()}
num_classes = max(a_to_n.values()) + 1
for subset in ['training', 'validation']:
vids = [line.strip() for line in open('{}/{}_videos.csv'.format(args.anno_path, subset))]
anno_per_video = {}
for line in open('{}/{}.csv'.format(args.anno_path, subset)):
idx, vid, f_start, f_end, c_verb, c_noun, c_verbnoun = line.strip().split(',')
vid = vid.strip()
if vid not in anno_per_video:
anno_per_video[vid] = [(int(f_start), int(f_end), int(c_verb), int(c_noun), int(c_verbnoun))]
else:
anno_per_video[vid].append((int(f_start), int(f_end), int(c_verb), int(c_noun), int(c_verbnoun)))
for vid in vids:
feature = np.load('{}/features/rgb_kinetics_resnet50/{}.npy'.format(args.data_path, vid))
feature_len = feature.shape[0]
targets[vid] = np.zeros((feature_len,
num_classes))
targets[vid][:, 0] = 1
for anno in anno_per_video[vid]:
print(vid, anno)
ss = int(np.floor(anno[0] / args.tick))
ee = min(int(np.ceil(anno[1] / args.tick)), feature_len)
if args.class_type == 'action':
ci = anno[4]
elif args.class_type == 'verb':
ci = anno[2]
elif args.class_type == 'noun':
ci = anno[3]
for si in range(ss, ee):
targets[vid][si, ci + 1] = 1
targets[vid][si, 0] = 0
print('Dumping npy files..')
for vid in targets:
np.save(args.targets_path + '{}.npy'.format(vid), targets[vid])
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,45 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import os
from rekognition_online_action_detection.engines import do_inference
from rekognition_online_action_detection.models import build_model
from rekognition_online_action_detection.utils.checkpointer import \
setup_checkpointer
from rekognition_online_action_detection.utils.env import setup_environment
from rekognition_online_action_detection.utils.logger import setup_logger
from rekognition_online_action_detection.utils.parser import load_cfg
from utils import validate_cfg
def main(cfg):
validate_cfg(cfg)
config_name = os.path.basename(cfg.OUTPUT_DIR)
print(f"cfg.MODEL.CHECKPOINT: {cfg.MODEL.CHECKPOINT}")
print(f"cfg.OUTPUT_DIR: {cfg.OUTPUT_DIR}")
# Setup configurations
device = setup_environment(cfg)
checkpointer = setup_checkpointer(cfg, phase='test')
logger = setup_logger(cfg, phase='test')
print(cfg)
print(f"DATA.IGNORE_INDEX: {cfg.DATA.IGNORE_INDEX}")
# Build model
model = build_model(cfg, device)
# Load pretrained model
checkpointer.load(model)
do_inference(
cfg,
model,
device,
logger,
)
if __name__ == '__main__':
main(load_cfg())

View File

@@ -0,0 +1,67 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import os
import time
import wandb
from rekognition_online_action_detection.criterions import build_criterion
from rekognition_online_action_detection.datasets import build_data_loader
from rekognition_online_action_detection.engines import do_train
from rekognition_online_action_detection.models import build_model
from rekognition_online_action_detection.optimizers import (build_optimizer,
build_scheduler)
from rekognition_online_action_detection.utils.checkpointer import \
setup_checkpointer
from rekognition_online_action_detection.utils.env import setup_environment
from rekognition_online_action_detection.utils.logger import setup_logger
from rekognition_online_action_detection.utils.parser import load_cfg
from utils import parse_config
def main(cfg):
parse_config(cfg)
# Setup configurations
device = setup_environment(cfg)
checkpointer = setup_checkpointer(cfg, phase="train")
logger = setup_logger(cfg, phase="train")
print(cfg)
print(f"cfg['SOLVER']['NUM_EPOCHS']: {cfg['SOLVER']['NUM_EPOCHS']}")
print(f"cfg['MODEL']['CHECKPOINT']: {cfg['MODEL']['CHECKPOINT']}")
# Build data loaders
data_loaders = {phase: build_data_loader(cfg, phase) for phase in cfg.SOLVER.PHASES}
# Build model
model = build_model(cfg, device)
# Build criterion
criterion = build_criterion(cfg, device)
# Build optimizer
optimizer = build_optimizer(cfg, model)
# Load pretrained model and optimizer
checkpointer.load(model, optimizer)
# Build scheduler
scheduler = build_scheduler(cfg, optimizer, len(data_loaders["train"]))
do_train(
cfg,
data_loaders,
model,
criterion,
optimizer,
scheduler,
device,
checkpointer,
logger,
)
wandb.finish() if cfg.USE_WANDB else None
if __name__ == "__main__":
main(load_cfg())

View File

@@ -0,0 +1,97 @@
import os
import wandb
def parse_config(cfg):
modality = get_modality(cfg)
cfg_filename = os.path.basename(cfg.OUTPUT_DIR)
os.environ["WANDB__SERVICE_WAIT"] = "3000"
cfg_filename_abbr = cfg_filename.split('_1x_')[-1]
wandb_postfix = generate_wandb_postfix(cfg, modality)
scheduler_name = cfg.SOLVER.SCHEDULER.SCHEDULER_NAME
learning_rate = cfg.SOLVER.BASE_LR
weight_decay = cfg.SOLVER.WEIGHT_DECAY
cfg_filename = cfg_filename.replace('long_512', f'long_{cfg.MODEL.LSTR.LONG_MEMORY_SECONDS}')
cfg_filename = cfg_filename.replace('work_8', f'work_{cfg.MODEL.LSTR.WORK_MEMORY_SECONDS}')
cfg.OUTPUT_DIR = os.path.join(os.path.dirname(cfg.OUTPUT_DIR), cfg_filename)
if cfg.SAVE_HPARAM:
# save different dir containing hparam
hparam_info = '_'.join([f"lr_{learning_rate}", f"wd_{weight_decay}", f"schd_{scheduler_name}"])
cfg.OUTPUT_DIR = os.path.join(cfg.OUTPUT_DIR, hparam_info, modality)
print(f"HPARAM: {hparam_info}")
print(f"cfg.OUTPUT_DIR: {cfg.OUTPUT_DIR}")
wandb_name = cfg_filename_abbr + '_' + wandb_postfix
if cfg.SAVE_HPARAM:
wandb_name += '_' + '_'.join([f"lr_{learning_rate}", f"wd_{weight_decay}", f"schd_{scheduler_name}"])
if cfg.USE_WANDB:
print("*** init wandb ***")
print(f"wandb_name: {wandb_name}")
def get_modality(cfg):
modality = ''
if 'motion' in cfg.INPUT.MODALITY:
modality += 'A'
if 'visual' in cfg.INPUT.MODALITY:
modality += 'V'
if 'object' in cfg.INPUT.MODALITY:
modality += 'T'
elif cfg['INPUT']['MODALITY'] == 'twostream':
modality = 'AV'
if modality == '':
raise ValueError(f"Unknown modality: {cfg['INPUT']['MODALITY']}")
return modality
def generate_wandb_postfix(cfg, modality):
visual_feature_type = '_'.join(cfg['INPUT']['VISUAL_FEATURE'].split('_')[1:])
audio_feature_type = '_'.join(cfg['INPUT']['MOTION_FEATURE'].split('_')[1:])
text_feature_type = '_'.join(cfg['INPUT']['OBJECT_FEATURE'].split('_')[1:])
if cfg.MODEL.LSTR.LONG_MEMORY_USE_PE == True:
model_name = 'LSTR_TESTRA'
else:
model_name = 'TESTRA'
if '_aa' in cfg.OUTPUT_DIR:
model_name += '_aa'
name_parts = [
model_name,
cfg['DATA']['DATA_NAME'],
modality,
# visual_feature_type,
# audio_feature_type,
# text_feature_type,
# f"epoch_{cfg['SOLVER']['NUM_EPOCHS']}"
]
if cfg['MODEL']['CHECKPOINT']:
name_parts.append(f"ckpt_{os.path.basename(cfg['MODEL']['CHECKPOINT']).split('.')[0]}")
return '_'.join(name_parts)
def get_wandb_name(cfg, cfg_filename, modality):
cfg_filename_abbr = cfg_filename.split('_1x_')[-1]
wandb_postfix = generate_wandb_postfix(cfg, modality)
scheduler_name = cfg.SOLVER.SCHEDULER.SCHEDULER_NAME
learning_rate = cfg.SOLVER.BASE_LR
weight_decay = cfg.SOLVER.WEIGHT_DECAY
wandb_name = cfg_filename_abbr + '_' + wandb_postfix + '_' + '_'.join([f"lr_{learning_rate}", f"wd_{weight_decay}", f"schd_{scheduler_name}"])
return wandb_name
def validate_cfg(cfg):
if cfg.DATA.NUM_CLASSES != len(cfg.DATA.CLASS_NAMES):
print(f"len(cfg.DATA.CLASS_NAMES): {len(cfg.DATA.CLASS_NAMES)} is not equal to cfg.DATA.NUM_CLASSES: {cfg.DATA.NUM_CLASSES}")
cfg.DATA.CLASS_NAMES = cfg.DATA.CLASS_NAMES[:cfg.DATA.NUM_CLASSES]
print(f"Adjusted cfg.DATA.CLASS_NAMES: {cfg.DATA.CLASS_NAMES}")

View File

@@ -0,0 +1,27 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
# PyTorch models
*.pth
# Miscellaneous
*.DS_Store
*.swp
*.pyc
*.pkl
*.out
dataset/
# Dataset soft links
data/
# Output files
output/
output_*/
checkpoints/
notebooks/
wandb/

View File

@@ -0,0 +1,32 @@
model: 'MambaA'
data_name: 'EasyCom_ANTICIPATION'
task: 'ANTICIPATION'
loss: 'ANTICIPATION' # NONUNIFORM, originally
metric: 'AP' # AP or cAP
optimizer: 'AdamW'
device: 'cuda'
feature_pretrained: 'resnet50'
root_path: './data/EasyCom'
rgb_type: 'rgb_kinetics_resnet50'
flow_type: 'audio_wav2vec2'
annotation_type: 'target_perframe'
video_list_path: 'data_info/data_info.json'
output_path: './output_cfg'
window_size: 128
batch_size: 64
test_batch_size: 1
# loss_offset: 4 # loss weight a_t when L - {loss_offset} <= t <= L
num_epoch: 30
lr: 0.0001
weight_decay: 0.05
num_workers: 4
dropout: 0.20
num_classes: 3
embedding_dim: 2048
hidden_dim: 1024
num_layers: 1
stride: 4
anticipation_length: 10
actionness: False
ignore_index: -100
seed: 20

View File

@@ -0,0 +1,32 @@
model: 'MiniROADA'
data_name: 'EasyCom_ANTICIPATION'
task: 'ANTICIPATION'
loss: 'ANTICIPATION' # NONUNIFORM, originally
metric: 'AP' # AP or cAP
optimizer: 'AdamW'
device: 'cuda'
feature_pretrained: 'resnet50'
root_path: './data/EasyCom'
rgb_type: 'rgb_kinetics_resnet50'
flow_type: 'audio_wav2vec2'
annotation_type: 'target_perframe'
video_list_path: 'data_info/data_info.json'
output_path: './output_cfg'
window_size: 128
batch_size: 64
test_batch_size: 1
# loss_offset: 4 # loss weight a_t when L - {loss_offset} <= t <= L
num_epoch: 30
lr: 0.0001
weight_decay: 0.05
num_workers: 4
dropout: 0.20
num_classes: 3 # [background, target speaker speaking, other person speaking]
embedding_dim: 2048
hidden_dim: 1024
num_layers: 1
stride: 4
anticipation_length: 10
actionness: False
ignore_index: -100
seed: 20

View File

@@ -0,0 +1,32 @@
model: 'MambaA'
data_name: 'YTConv_ANTICIPATION'
data_info_name: 'shard_0'
task: 'ANTICIPATION'
loss: 'ANTICIPATION' # NONUNIFORM, originally
metric: 'AP' # AP or cAP
optimizer: 'AdamW'
device: 'cuda'
feature_pretrained: 'resnet50'
root_path: './data/YTConv/shard_0'
rgb_type: 'rgb_kinetics_resnet50'
flow_type: 'audio_wav2vec2' # or 'none'?
annotation_type: 'target_perframe/case1'
video_list_path: 'data_info/YTConv_info.json'
output_path: './output_cfg'
window_size: 128
batch_size: 32
test_batch_size: 1
# loss_offset: 4 # loss weight a_t when L - {loss_offset} <= t <= L
num_epoch: 30
lr: 0.0001
weight_decay: 0.05
num_workers: 4
dropout: 0.20
num_classes: 2 # for YTConv pretraining, there is no
embedding_dim: 2048
hidden_dim: 1024
num_layers: 1
stride: 4
anticipation_length: 10
actionness: False
ignore_index: -100

View File

@@ -0,0 +1,32 @@
model: 'MiniROADA'
data_name: 'YTConv_ANTICIPATION'
data_info_name: 'shard_0'
task: 'ANTICIPATION'
loss: 'ANTICIPATION' # NONUNIFORM, originally
metric: 'AP' # AP or cAP
optimizer: 'AdamW'
device: 'cuda'
feature_pretrained: 'resnet50'
root_path: './data/YTConv/shard_0'
rgb_type: 'rgb_kinetics_resnet50'
flow_type: 'audio_wav2vec2'
annotation_type: 'target_perframe'
video_list_path: 'data_info/YTConv_info.json'
output_path: './output_cfg'
window_size: 128
batch_size: 64
test_batch_size: 1
# loss_offset: 4 # loss weight a_t when L - {loss_offset} <= t <= L
num_epoch: 30
lr: 0.0001
weight_decay: 0.05
num_workers: 4
dropout: 0.20
num_classes: 2 # for pretraining, there is no target speaker
embedding_dim: 2048
hidden_dim: 1024
num_layers: 1
stride: 4
anticipation_length: 10
actionness: False
ignore_index: -100

View File

View File

@@ -0,0 +1,36 @@
model: 'MambaA'
data_name: 'EGO4D_ANTICIPATION'
data_info_name: 'EGO4DSHUFFLE'
task: 'ANTICIPATION'
loss: 'ANTICIPATION' # NONUNIFORM, originally
metric: 'AP' # AP or cAP
optimizer: 'AdamW'
device: 'cuda'
feature_pretrained: 'resnet50'
root_path: './data/EGO4D'
rgb_type: 'rgb_kinetics_resnet50'
flow_type: 'audio_wav2vec2'
annotation_type: 'target_perframe'
video_list_path: 'data_info/data_info.json'
output_path: './output_cfg'
window_size: 128
batch_size: 64
test_batch_size: 1
# loss_offset: 4 # loss weight a_t when L - {loss_offset} <= t <= L
num_epoch: 30
lr: 0.0001
weight_decay: 0.05
num_workers: 4
dropout: 0.20
num_classes: 3 # including background
embedding_dim: 2048
hidden_dim: 1024
d_state: 16
d_conv: 4
expand: 2
num_layers: 1
stride: 4
anticipation_length: 10
actionness: False
ignore_index: -100
seed: 20

View File

@@ -0,0 +1,32 @@
model: 'MiniROADA'
data_name: 'EGO4D_ANTICIPATION'
data_info_name: 'EGO4DSHUFFLE'
task: 'ANTICIPATION'
loss: 'ANTICIPATION' # NONUNIFORM, originally
metric: 'AP' # AP or cAP
optimizer: 'AdamW'
device: 'cuda'
feature_pretrained: 'resnet50'
root_path: './data/EGO4D'
rgb_type: 'rgb_kinetics_resnet50'
flow_type: 'audio_wav2vec2' # or 'none'?
annotation_type: 'target_perframe'
video_list_path: 'data_info/data_info.json'
output_path: './output_cfg'
window_size: 128
batch_size: 64
test_batch_size: 1
# loss_offset: 4 # loss weight a_t when L - {loss_offset} <= t <= L
num_epoch: 30
lr: 0.0001
weight_decay: 0.05
num_workers: 4
dropout: 0.20
num_classes: 3
embedding_dim: 2048
hidden_dim: 1024
num_layers: 1
stride: 4
anticipation_length: 10
actionness: False
ignore_index: -100

View File

@@ -0,0 +1,26 @@
# dimension of features extracted from different models
# used in feature_head, dataset, models
FEATURE_SIZES = {
'rgb_anet_resnet50': 2048,
'flow_anet_resnet50': 2048,
'rgb_kinetics_bninception': 1024,
'flow_kinetics_bninception': 1024,
'rgb_farl': 512,
'rgb_kinetics_resnet50': 2048,
'rgb_kinetics_resnet50_debug': 2048,
'rgb_kinetics_resnet50_debug_resized': 2048,
'flow_kinetics_resnet50': 2048,
'flow_nv_kinetics_bninception': 1024,
'rgb_kinetics_i3d': 2048,
'flow_kinetics_i3d': 2048,
'audio_wav2vec2': 5120,
'audio_wav2vec2_left': 5120,
'audio_wav2vec2_right': 5120,
'text_word2vec': 300,
'text_fasttext_crawl': 300,
'text_gpt2_last_subword_window_size_0': 768,
'text_gpt2_last_subword_window_size_20': 768,
'text_gpt2_last_subword_window_size_50': 768,
'text_gpt2_last_subword_window_size_100': 768,
}

View File

@@ -0,0 +1,2 @@
from .loss import OadLoss
from .loss_builder import build_criterion

View File

@@ -0,0 +1,205 @@
import torch
import torch.nn as nn
import torch.nn.functional as F
from criterions.loss_builder import CRITERIONS
@CRITERIONS.register('NONUNIFORM')
class OadLoss(nn.Module):
def __init__(self, cfg, reduction='mean'):
super(OadLoss, self).__init__()
self.reduction = reduction
self.num_classes = cfg['num_classes']
self.loss_offset = cfg['loss_offset'] if 'loss_offset' in cfg else 0
assert self.loss_offset==0, f'loss_offset is not 0: {self.loss_offset}'
self.loss = self.end_loss
def end_loss(self, out_dict, target):
# logits: (B, seq, K) target: (B, seq, K)
assert self.loss_offset < out_dict['logits'].shape[1], f'loss_offset is longer than the sequence loss_offset: {self.loss_offset}, logits.shape: {out_dict["logits"].shape}'
logits = out_dict['logits']
logits = logits[:,-1 - self.loss_offset,:].contiguous()
target = target[:,-1 - self.loss_offset,:].contiguous()
ce_loss = self.mlce_loss(logits, target)
return ce_loss
def mlce_loss(self, logits, target):
'''
multi label cross entropy loss.
logits: (B, K) target: (B, K)
'''
logsoftmax = nn.LogSoftmax(dim=-1).to(logits.device)
output = torch.sum(-F.normalize(target) * logsoftmax(logits), dim=1) # B
if self.reduction == 'mean':
loss = torch.mean(output)
elif self.reduction == 'sum':
loss = torch.sum(output)
return loss
def forward(self, out_dict, target):
return self.loss(out_dict, target)
@CRITERIONS.register('UNIFORM')
class OadUniformLoss(nn.Module):
def __init__(self, cfg, reduction='mean'):
super(OadUniformLoss, self).__init__()
self.reduction = reduction
self.num_classes = cfg['num_classes']
# self.loss_offset = cfg['loss_offset'] if 'loss_offset' in cfg else 0
self.loss = self.end_loss
def end_loss(self, out_dict, target):
# logits: (B, seq, K) target: (B, seq, K)
logits = out_dict['logits']
# logits = logits[:,-1 - self.loss_offset,:].contiguous()
# target = target[:,-1 - self.loss_offset,:].contiguous()
ce_loss = self.mlce_loss(logits, target)
return ce_loss
def mlce_loss(self, logits, target):
'''
multi label cross entropy loss.
logits: (B, K) target: (B, K)
'''
logsoftmax = nn.LogSoftmax(dim=-1).to(logits.device)
output = torch.sum(-target * logsoftmax(logits), dim=1) # B
# output = torch.sum(-F.normalize(target) * logsoftmax(logits), dim=1) # B
if self.reduction == 'mean':
loss = torch.mean(output)
elif self.reduction == 'sum':
loss = torch.sum(output)
return loss
def forward(self, out_dict, target):
return self.loss(out_dict, target)
@CRITERIONS.register('OADTR')
class OadTRLoss(nn.Module):
def __init__(self, cfg, reduction='mean'):
super(OadTRLoss, self).__init__()
self.reduction = reduction
self.num_classes = cfg['num_classes']
# self.loss_offset = cfg['loss_offset'] if 'loss_offset' in cfg else 0
self.loss = self.end_loss
def end_loss(self, out_dict, target):
# logits: (B, seq, K) target: (B, seq, K)
assert self.loss_offset < out_dict['logits'].shape[1], f'loss_offset is longer than the sequence loss_offset: {self.loss_offset}, logits.shape: {out_dict["logits"].shape}'
logits = out_dict['logits']
# logits = logits[:,-1 - self.loss_offset,:].contiguous()
# target = target[:,-1 - self.loss_offset,:].contiguous()
ce_loss = self.mlce_loss(logits, target)
return ce_loss
def mlce_loss(self, logits, target):
'''
multi label cross entropy loss.
logits: (B, K) target: (B, K)
'''
logsoftmax = nn.LogSoftmax(dim=-1).to(logits.device)
output = torch.sum(-F.normalize(target) * logsoftmax(logits), dim=1) # B
if self.reduction == 'mean':
loss = torch.mean(output)
elif self.reduction == 'sum':
loss = torch.sum(output)
return loss
def forward(self, out_dict, enc_target, dec_target):
return self.loss(out_dict, enc_target, dec_target)
@CRITERIONS.register('ANTICIPATION')
class OadAntLoss(nn.Module):
# reduction was sum, not mean, what if we change to sum?
def __init__(self, cfg, reduction='mean'):
super(OadAntLoss, self).__init__()
self.reduction = reduction
self.loss = self.anticipation_loss
self.num_classes = cfg['num_classes']
def anticipation_loss(self, out_dict, target, ant_target):
# anticipation_logits: (B, seq, ANTICIPATION_LENGTH, num_class)
# ant_target: (B, ANTICIPATION_LENGTH, num_class)
anticipation_logits = out_dict['anticipation_logits']
# [B, window_size, anticipation_length, num_class]
pred_anticipation_logits = anticipation_logits[:,-1,:,:].contiguous().view(-1, self.num_classes)
anticipation_logit_targets = ant_target.view(-1, self.num_classes)
ant_loss = self.mlce_loss(pred_anticipation_logits, anticipation_logit_targets)
return ant_loss
def ce_loss(self, out_dict, target):
# logits: (B, seq, K) target: (B, seq, K)
logits = out_dict['logits']
logits = logits[:,-1,:].contiguous()
target = target[:,-1,:].contiguous()
ce_loss = self.mlce_loss(logits, target)
return ce_loss
def mlce_loss(self, logits, target):
'''
multi label cross entropy loss.
logits: (B, K) target: (B, K)
'''
logsoftmax = nn.LogSoftmax(dim=-1).to(logits.device)
output = torch.sum(-F.normalize(target) * logsoftmax(logits), dim=1) # B
if self.reduction == 'mean':
loss = torch.mean(output)
elif self.reduction == 'sum':
loss = torch.sum(output)
return loss
def forward(self, out_dict, target, ant_target):
return self.loss(out_dict, target, ant_target)
@CRITERIONS.register('UNIFORM_ANTICIPATION')
class OadUniformAntLoss(nn.Module):
# reduction was sum, not mean, what if we change to sum?
def __init__(self, cfg, reduction='mean'):
super(OadUniformAntLoss, self).__init__()
self.reduction = reduction
self.loss = self.anticipation_loss
self.num_classes = cfg['num_classes']
def anticipation_loss(self, out_dict, target, ant_target):
anticipation_logits = out_dict['anticipation_logits']
pred_anticipation_logits = anticipation_logits[:,-1,:,:].contiguous().view(-1, self.num_classes)
anticipation_logit_targets = ant_target.view(-1, self.num_classes)
ant_loss = self.mlce_loss(pred_anticipation_logits, anticipation_logit_targets)
return ant_loss
def ce_loss(self, out_dict, target):
# logits: (B, seq, K) target: (B, seq, K)
logits = out_dict['logits']
logits = logits[:,-1,:].contiguous()
target = target[:,-1,:].contiguous()
ce_loss = self.mlce_loss(logits, target)
return ce_loss
def mlce_loss(self, logits, target):
'''
multi label cross entropy loss.
logits: (B, K) target: (B, K)
'''
logsoftmax = nn.LogSoftmax(dim=-1).to(logits.device)
output = torch.sum(-target * logsoftmax(logits), dim=1) # B
# output = torch.sum(-F.normalize(target) * logsoftmax(logits), dim=1) # B
if self.reduction == 'mean':
loss = torch.mean(output)
elif self.reduction == 'sum':
loss = torch.sum(output)
return loss
def forward(self, out_dict, target, ant_target):
return self.loss(out_dict, target, ant_target)

View File

@@ -0,0 +1,11 @@
__all__ = [
'build_criterion'
]
from utils import Registry
CRITERIONS = Registry()
def build_criterion(cfg, device=None):
criterion = CRITERIONS[cfg["loss"]](cfg)
return criterion.to(device)

View File

@@ -0,0 +1,426 @@
{
"shard_0": {
"class_index": [
"background",
"camera wearer start to speak"
],
"train_session_set": [
"m5Aoywf51L4",
"p99cIB7nHjw",
"zGgMzBfPfaI",
"O0eCtKU3FxY",
"goZQ6iOIFYo",
"9Tb-lSyhgoE",
"GBxQtGUjAMA",
"VXgt7keOC8I",
"hZ2tin2J0P4",
"Ur-P18sm4tI",
"WfYmsNBQwg8",
"AvMNaDDfrN0",
"fW7pCN3wwdo",
"xG6HBdTtS4s",
"HcDlqUeDD9Q",
"1DE3mKncHUA",
"oEMX_quSMZY",
"jmS3nWbGV5Y",
"qUJRjPRH-u4",
"aHsUgWfUcss",
"ZfdQOmJkaK0",
"4f43nYrfTnI",
"qE1FnzkNjrE",
"4NsmKhs9flk",
"3wDSJ9ABzmo",
"sY6_8QwRZsw",
"hByfAzEoeKA",
"vDEsV0AhcjM",
"XGNhX3Nu1RE",
"MkVT8K0V57A",
"yR8y-wN8Clk",
"P1dTOlO2sl8",
"6UGC_wxTeB0",
"d4Xud2Gqoxk",
"IJ1aqePculQ",
"o8Oxf5QBitQ",
"XVci_V4qxmc",
"ofDzhOS1Pv0",
"mygTXBuMRF0",
"iq6q82Jo8ZQ",
"Kzc_ekBJ4QE",
"WEeJ9B1i8HI",
"rciqpxdCc9Y",
"Rh7d2D7JVLI",
"Qkx4hSlc8jk",
"ixi7sVLFrWM",
"n96-scSyoOQ",
"YZ5xYioIQdA",
"X76QxuWm1oA",
"2I7d_LfK75Y",
"AeUWBaYFMyo",
"lsqXFgymLlI",
"Dx_0jYMWN5o",
"0raJ1-1BJ7s",
"EGX8am5hWh0",
"KjtZ_CLY1hQ",
"R4FQI2BI3hw",
"fAoqqtsNjbQ",
"l6RwyA6jUdM",
"GJW6dlagRnU",
"j_FGvmbp6aM",
"V2O15h1VrH8",
"P3c70VbDP3E",
"epAwpBdUiYU",
"6dpv_JeyL8w",
"dUKYxsvnuBM",
"L2_zn7h8sOQ",
"m09qdCkAhmY",
"7BB71tIae7A",
"6go4oViEkdc",
"MYimxshUK1c",
"v4eZXWcIRQU",
"vGI_w1Ml6S0",
"BeFndnkT00c",
"9JHRbL1jmq4",
"0rBaR5dd-Fs",
"EvhkIS_-K-w",
"pYVbLx-h5HI",
"fVciOXYDlqg",
"zWFH4N-CmDE",
"Aw4cauzfX5E",
"lxVpcQHtcWs",
"32dkF-aXgug",
"_USorgWOsUk",
"1yVMU93nmv8",
"aYkF4Ybk-l8",
"Vwt18p0BqzM",
"oOQM5Pvjut4",
"_WWSMNtNYx0",
"zzlhwg0IJ5Y",
"lJhIffkUhr8",
"PbS9nSH3mqM",
"8EGVgiFpwhg",
"5LhXpv5IFO4",
"ibD4APVzWgg",
"2nhcSQmopO4",
"gri79OfCRvo",
"lOwQN3QcRzE",
"wQyluRNtqFo",
"IZyx8zeiKB0",
"mZyNfKyzDHU",
"A9eGJWiuIKs",
"d37DZogot1w",
"7ZUSVabB6N4",
"m62GQXq-358",
"ArwPVsCZo2c",
"QK-BFkpO8s4",
"PgfX9bvmEhs",
"l1jTUhwWJYA",
"S1PGHO4evaI",
"M6uPJa5B8Ns",
"XXzz19J-1w8",
"U4uV1xmUaB0",
"skQ0OBpkKqY",
"mfW57TDpMtc",
"NAwKA75cDwE",
"eIEBI2_Y14Q",
"eWQGYpSoiYU",
"3DPIUWhYtn8",
"_elvaRJrhE0",
"PAA8IFhWJzM",
"1XDKqhq6bb0",
"ngi9gy3DzRY",
"Y0yONfuimeE",
"g9_cAgHa_4Q",
"BIG7jEw-j4w",
"TFdom0lkd-0",
"2zV2N8_DwWg",
"kTz7XB_jpB4",
"GuoglIuhNjY",
"L98U3mO38gQ",
"UfWDoRAzpC8",
"W6TakaXCIIQ",
"i6j1I6pha-M",
"issHFC8Gbio",
"4EzGoRkd6SI",
"QoS0GgF4sI4",
"A8hJg3D_TGY",
"A3N10suCRCw",
"6962tnWRsNY",
"lS4p8osXSJs",
"wbZ7ZhcZd6o",
"fsIvQvIlKwc",
"4jQSOSi2DA8",
"Vpt6tZBvXQU",
"kxOG3kpLmYw",
"Mzjlz0nq9AA",
"80cuXt2VSko",
"VvswhogSiY0",
"5Q6ky5VrOIA",
"I_XtcUpNRF8",
"QmtmTGycK9A",
"qp3x8iy7vkA",
"xGU8QDtPrQI",
"uWWUfqth_BA",
"WbM7w8r4VGQ",
"bt6Ed1qjulw",
"7vymT27scoY",
"b9AdV6oVDVg",
"nf2zqYUAOwg",
"A-LNJc-1Jc0",
"yCI-hpPiDVc",
"Uu5DME4FqIM",
"symjuH9iOTc",
"mPf2MTnTBc0",
"NDTlQ1KGvH8",
"g9vuDzFQSRU",
"B6iqnvleve0",
"9bbszAD7JL0",
"RIyMAuz7Ckg",
"mlrQHtBRMhc",
"3gaidlW7vig",
"DGdV97q9ndk",
"qthjlg0lXFg",
"gWng5YLPOow",
"iaYG-KL9XRM",
"385414AVZcA",
"7xhBnWGpI7c",
"Nh0cmSQUs-E",
"Rvtj0QHPulA",
"rAtLksYQukw",
"S9rB__zbbe0",
"SNpMVsOPfHM",
"zhF43F4kO2w",
"HOUkcInyxN0",
"x8ymkfr2Z1o",
"Ix6Khtq6O-k",
"VHs_JyPhmtE",
"zJX3byjYSMc",
"zdqX09zZNf8",
"uPwlmwNQq8Y",
"e4xNTXrk6wE",
"P2PNUWN4GMs",
"_WVHCYjHBVY",
"5Jtv1GpPvHM",
"AIZYucKOzhw",
"vbsR_VDZXh0",
"9Ew0lDHB0kM",
"ejpkPVZHuMM",
"47HlvCfubE4",
"HzPkQSIG2m0",
"e7mTRnNnOXg",
"eZHx_biEECg",
"FvP6WFKNP4Y",
"g_TJ5XFEVfY",
"8Utg2VEWsJg",
"VMdBjL-tpoo",
"zihP2Eqeqxk",
"ILVqgOlntLM",
"Z1BWQI8KPCg",
"j8TRVp-7l3Q",
"G5gJBZ-3OlY",
"o1JUlGSirSE",
"OsPdhUQ0snE",
"-5uIaW2gKWQ",
"03kiFNh-9ME",
"w5W6mgDaJJA",
"sYsgd_8AFoo",
"vLg8kVUTmWU",
"OK0J0xsuSWQ",
"d284rjoU4Cs",
"5lE6V4jrUl4",
"bS1OpC9xu48",
"ZUxpqU_J_n0",
"AUliC0c_YIQ",
"nJ58-d_viOo",
"uIy3b2kzlrM",
"vOTLfb0qjfY",
"37_7tPZjC34",
"MUwCdP5qceI",
"lYvNi_ewd8U",
"PDG7SzDBTC4",
"QHQzoHolWvI",
"YmvOEj00LB8",
"fLhkXVx7Yw0",
"Uj5nhugVgwo",
"aHbrDu2o0WY",
"yKW6vVgEPCM",
"Q0WUgyqQ-4I",
"xw3KOPJ9a3E",
"5mrTo7c3bCM",
"x0ft032W--4",
"UdQxEbrIocc",
"EHZHAFyv_Ks",
"7DojKgPRLVk",
"-1CahRD6lJ0",
"f3TuYwl7AZE",
"TgGTovCcS9Y",
"AwyrJGeIN_g",
"RbPJD4Wh-l8",
"58bFdwYkDzU",
"6gIEKGxL7zo",
"BeuzJwTuChQ",
"YebZyAzLZuc",
"9T74b8EQBVY",
"TqSVFjw1uNM",
"WUYhCmfE1a0",
"vRCVUQFUio4",
"w2t9JdMNMgQ",
"nn6-x9xXMeE",
"FDFVPzLlzFs",
"qFhvi4bH29E",
"qdpsMXLsUZM",
"JREk1QeSc9A",
"jOVEs-4r1jc",
"v4VZI9Ouzy0",
"DMg5UUvf-C4",
"26J5zRzQkOg",
"YkiJ4iJfJbw",
"_d8j8nfTEIk",
"wMNb3hQXEUE",
"1KI8ex22blk",
"eCp2Jp7MtKg",
"HNlpHBb5dC4",
"C6_pDRdIo1k",
"ZRWJJHrgP54",
"L7jfx4veKIA",
"p1a5_qltICM",
"nZ0svM8DoY0",
"miEzjCJVaZk",
"TpC2tDCxABw",
"SZmqoqCX__U",
"dZ0bYteaI2Q",
"GW-cYEzShmY",
"iUYuPhutKVw",
"97VSm6T2zMw",
"jPiQyi5jr1M",
"lrUi4nsPZ9M",
"rXLeXke-k_w",
"mEO4izUqZ7E",
"4qI1LjMXfh0",
"mVt6myGMa-0",
"VJrnhh6FqEg",
"vwCeBWbZvrE",
"c08fl8-xmVE",
"syZ3bvXIrpA",
"i3ysNaulop4",
"__t3rU0QjsE",
"jT32uQQVGvU",
"Fzvmh-R2EWk",
"QApJSKrLg6E",
"L5y3j8SdGSU",
"3t8JGblR__0",
"e1F90W40t6g",
"j6L8UIa5z28",
"Zmg2_C1FuVI",
"XIXKJXsiBFc",
"NVHB1VdWFo4",
"-x7-8f7bsl4",
"2FSxSiowkGI",
"jgFIBIeFve0",
"1BLuXK1M5CE",
"zmpx6LUT2ow",
"z-QUzJTsFro",
"0sS4v13DAK8",
"gMIBQ0mbRvM",
"9kcaEXlTB5s",
"ZYArPGMLS4k",
"tejW9GMaucA",
"z9r4bmXCWiI",
"4Zpsb46iaLc",
"52Odc8UbvC8",
"x-B6-iYDHPs",
"8GinSDc0Is8",
"5lwbp5d_w0Y",
"I4LpTQLzlHE",
"lMl6-XH7ozY",
"tOubscNbY1Q",
"7gb7AH-oWcQ",
"mDerqt9Ib-I",
"gKV3FNd9WHE",
"kH5L1E_r-3U",
"sq5TRFxVp0w",
"SrBhj7Xusk8",
"RxlxEapsz2E",
"aCD3_TziiKo",
"_HPVZOkxdkk",
"pNCjgVQ53Y8",
"iPdT5GUoGvA",
"cIrlet1oRw4",
"1dyWUMBjMwQ",
"XZQB3cq18yc",
"i75Mn_AeFBY",
"_0VwR9WPS-k",
"q1JDjgnRdGY",
"1JNEpfvRq0Y",
"2FGj6HciQZA",
"eLi59TtNwHY",
"Z3W8OST7tmA",
"F2dNYov8-xw",
"kPfyJu_CrSA",
"7I4Y2HtAwlY",
"us-andJDfWw",
"Q0Bzn1teWfo",
"pADxBfOrYeQ",
"KnA1tIR0qCE",
"lBNPfBcb6pw",
"zt9MIiBfxF8",
"FwX12hPqCws",
"6e2LXGrze2M",
"0dvclhcFjSo",
"UTkuDYOIxKo",
"4n044cOTiNg",
"VxksG15OgRY",
"4193MkWdJcQ",
"2bDX4Uzy8Wc",
"SkyBNX2auEc",
"JrSLspuJ2gk",
"GZO7V-CWSJ8",
"TYMIy4qlRzc",
"jK7q_Li7LU0",
"UiTIXDQEkPM"
],
"test_session_set": [
"DTGtC7FC4oI",
"CIWvk3gJ_7E",
"A1fZ6rlOwxk",
"cB8WCrmJEEA",
"gGQ_2Ank9-U",
"b91abcyMeNY",
"9MuaFeyczM0",
"rwZxhdK_UVU",
"hd_WbrqYnTE",
"4BWcqA9cFFk",
"MuOkgCkob6E",
"OdhFWA3Fgb0",
"Pe3ZabrUiUk",
"U0c_uv8DU08",
"kTqt_3YJBbs",
"eWXVVMi4bf8",
"RCD-vXumMIs",
"DuwiezJCZ3I",
"BNgEqjAiKU4",
"Cbn0t7eqaEs",
"D3M9Q0WwuFY",
"oO-Bv4iRZKI",
"bBvqtbZdSZQ",
"-mnoItCi32Y",
"yQPxKfz-bwI",
"ITVZ4cx5big",
"uY5_e0L7mjw",
"J_Pl9ftnNXE",
"Jt_pb40MRy4",
"E3hAj2niRNo",
"kl2xVSxVECQ",
"gzebuFs90Xg",
"6DQSIzJ9J-g",
"pJJlnryTYWg",
"X87qsfVLFoc",
"W7BIPWU1WHc",
"Oj506EVOiQc",
"DPEvWvcstxA",
"Pk16UT5-zeM",
"XMK1KEk5OdU",
"PpNm5llsEW8",
"sAp3rvablGk"
]
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
from .dataset_builder import build_dataset, build_data_loader
from .dataset import THUMOSDataset, FINEACTIONDataset

View File

@@ -0,0 +1,314 @@
import gc
import json
import os.path as osp
import sys
import numpy as np
import torch
import torch.utils.data as data
from configs.feature_sizes import FEATURE_SIZES
from datasets.dataset_builder import DATA_LAYERS
from utils.util import parse_flow_type, parse_rgb_type
@DATA_LAYERS.register("THUMOS")
@DATA_LAYERS.register("TVSERIES")
@DATA_LAYERS.register("EasyCom")
@DATA_LAYERS.register("EGO4D")
@DATA_LAYERS.register("IEMOCAP")
@DATA_LAYERS.register("IEMOCAP_DEBUG")
@DATA_LAYERS.register("YTConv")
class THUMOSDataset(data.Dataset):
def __init__(self, cfg, mode='train'):
self.root_path = cfg['root_path']
self.mode = mode
self.training = mode == 'train'
self.window_size = cfg['window_size']
self.enc_steps = cfg['window_size'] if 'window_size' in cfg else None
self.dec_steps = cfg['query_num'] if 'query_num' in cfg else None
self.stride = cfg['stride']
data_name = cfg['data_name']
self.model = cfg['model']
self.vids = json.load(open(cfg['video_list_path']))[data_name][mode + '_session_set']
self.num_classes = cfg['num_classes']
self.inputs = []
self._load_features(cfg)
self._init_features()
def _load_features(self, cfg):
self.annotation_type = cfg['annotation_type']
self.rgb_type = cfg['rgb_type']
self.flow_type = cfg['flow_type']
self.target_all = {}
self.rgb_inputs = {}
self.flow_inputs = {}
dummy_target = np.zeros((self.window_size-1, self.num_classes))
rgb_type = parse_rgb_type(cfg['rgb_type'])
flow_type = parse_flow_type(cfg['flow_type'])
dummy_rgb = np.zeros((self.window_size-1, FEATURE_SIZES[rgb_type]))
dummy_flow = np.zeros((self.window_size-1, FEATURE_SIZES[flow_type]))
for vid in self.vids:
target = np.load(osp.join(self.root_path, self.annotation_type, vid + '.npy'))
rgb = np.load(osp.join(self.root_path, self.rgb_type, vid + '.npy'))
flow = np.load(osp.join(self.root_path, self.flow_type, vid + '.npy'))
if self.training:
self.target_all[vid] = np.concatenate((dummy_target, target), axis=0)
self.rgb_inputs[vid] = np.concatenate((dummy_rgb, rgb), axis=0)
self.flow_inputs[vid] = np.concatenate((dummy_flow, flow), axis=0)
else:
self.target_all[vid] = target
self.rgb_inputs[vid] = rgb
self.flow_inputs[vid] = flow
def _init_features(self):
del self.inputs
gc.collect()
self.inputs = []
if self.model == 'MiniROAD':
for vid in self.vids:
target = self.target_all[vid]
if self.training:
seed = np.random.randint(self.stride)
for start, end in zip(range(seed, target.shape[0], self.stride),
range(seed + self.window_size, target.shape[0]+1, self.stride)):
self.inputs.append([
vid, start, end, target[start:end]
])
else:
start = 0
end = target.shape[0]
self.inputs.append([
vid, start, end, target[start:end]
])
elif self.model == 'OADTR':
for vid in self.vids:
target = self.target_all[vid]
seed = np.random.randint(self.stride) if self.training else 0
for start, end in zip(
range(seed, target.shape[0], 1),
range(seed + self.window_size, target.shape[0]+1, 1)
):
enc_target = target[start:end]
dec_target = target[end:end + self.dec_steps]
distance_target, class_h_target = self.get_distance_target(target[start:end])
self.inputs.append([
vid, start, end, enc_target, distance_target, class_h_target, dec_target
])
else:
for vid in self.vids:
target = self.target_all[vid]
seed = np.random.randint(self.stride) if self.training else 0
for start, end in zip(
range(seed, target.shape[0], 1),
range(seed + self.window_size, target.shape[0]+1, 1)
):
self.inputs.append([
vid, start, end, target[start:end]
])
def get_distance_target(self, target_vector):
target_matrix = np.zeros(self.enc_steps - 1)
target_argmax = target_vector[self.enc_steps-1].argmax()
for i in range(self.enc_steps - 1):
if target_vector[i].argmax() == target_argmax:
target_matrix[i] = 1.
return target_matrix, target_vector[self.enc_steps-1]
def __getitem__(self, index):
if self.model == 'MiniROAD' or self.model == 'Transformer':
vid, start, end, target = self.inputs[index]
rgb_input = self.rgb_inputs[vid][start:end]
flow_input = self.flow_inputs[vid][start:end]
rgb_input = torch.tensor(rgb_input.astype(np.float32))
flow_input = torch.tensor(flow_input.astype(np.float32))
target = torch.tensor(target.astype(np.float32))
return vid, rgb_input, flow_input, target
elif self.model == 'OADTR':
vid, start, end, enc_target, distance_target, class_h_target, dec_target = self.inputs[index]
rgb_input = self.rgb_inputs[vid][start:end]
flow_input = self.flow_inputs[vid][start:end]
rgb_input = torch.tensor(rgb_input.astype(np.float32))
flow_input = torch.tensor(flow_input.astype(np.float32))
enc_target = torch.tensor(enc_target.astype(np.float32))
distance_target = torch.tensor(distance_target.astype(np.float32))
class_h_target = torch.tensor(class_h_target.astype(np.float32))
dec_target = torch.tensor(dec_target.astype(np.float32))
return vid, rgb_input, flow_input, enc_target, distance_target, class_h_target, dec_target
def __len__(self):
return len(self.inputs)
@DATA_LAYERS.register("THUMOS_ANTICIPATION")
@DATA_LAYERS.register("TVSERIES_ANTICIPATION")
@DATA_LAYERS.register("IEMOCAP_ANTICIPATION")
@DATA_LAYERS.register("EasyCom_ANTICIPATION")
@DATA_LAYERS.register("YTConv_ANTICIPATION")
@DATA_LAYERS.register("EGO4D_ANTICIPATION")
class THUMOSDataset(data.Dataset):
def __init__(self, cfg, mode='train'):
self.cfg = cfg
self.root_path = cfg['root_path']
self.mode = mode
self.training = mode == 'train'
self.window_size = cfg['window_size']
self.stride = cfg['stride']
self.anticipation_length = cfg['anticipation_length']
data_name = cfg["data_name"].split('_')[0]
if 'data_info_name' in cfg.keys() and cfg['data_info_name'] is not None:
data_name = cfg['data_info_name']
self.vids = json.load(open(cfg['video_list_path']))[data_name][mode + '_session_set']
print(f'data_name: {data_name}')
print(f'{mode} session set length: {len(self.vids)}')
self.num_classes = cfg['num_classes']
self.inputs = []
self._load_features(cfg)
self._init_features()
def _load_features(self, cfg):
self.annotation_type = cfg['annotation_type']
self.rgb_type = cfg['rgb_type']
self.flow_type = cfg['flow_type']
self.target_all = {}
self.rgb_inputs = {}
self.flow_inputs = {}
dummy_target = np.zeros((self.window_size-1, self.num_classes))
dummy_rgb = np.zeros((self.window_size-1, FEATURE_SIZES[cfg['rgb_type']]))
dummy_flow = np.zeros((self.window_size-1, FEATURE_SIZES[cfg['flow_type']]))
for vid in self.vids:
target = np.load(osp.join(self.root_path, self.annotation_type, vid + '.npy'))
rgb = np.load(osp.join(self.root_path, self.rgb_type, vid + '.npy'))
flow = np.load(osp.join(self.root_path, self.flow_type, vid + '.npy'))
if self.training:
self.target_all[vid] = np.concatenate((dummy_target, target), axis=0)
self.rgb_inputs[vid] = np.concatenate((dummy_rgb, rgb), axis=0)
self.flow_inputs[vid] = np.concatenate((dummy_flow, flow), axis=0)
else:
self.target_all[vid] = target
self.rgb_inputs[vid] = rgb
self.flow_inputs[vid] = flow
def _init_features(self):
del self.inputs
gc.collect()
self.inputs = []
for vid in self.vids:
target = self.target_all[vid]
if self.training:
seed = np.random.randint(self.stride)
for start, end in zip(range(seed, target.shape[0], self.stride),
range(seed + self.window_size, target.shape[0]-self.anticipation_length, self.stride)):
self.inputs.append([
vid, start, end, target[start:end], target[end:end+self.anticipation_length]
])
else:
start = 0
end = target.shape[0] - self.anticipation_length
ant_target = []
for s in range(1, target.shape[0] - self.anticipation_length + 1):
ant_target.append(target[s:s + self.anticipation_length])
self.inputs.append([
vid, start, end, target[start:end], np.array(ant_target)
])
def __getitem__(self, index):
vid, start, end, target, ant_target = self.inputs[index]
rgb_input = self.rgb_inputs[vid][start:end]
flow_input = self.flow_inputs[vid][start:end]
rgb_input = torch.tensor(rgb_input.astype(np.float32))
flow_input = torch.tensor(flow_input.astype(np.float32))
target = torch.tensor(target.astype(np.float32))
ant_target = torch.tensor(ant_target.astype(np.float32))
return vid, rgb_input, flow_input, target, ant_target
def __len__(self):
return len(self.inputs)
@DATA_LAYERS.register("FINEACTION")
class FINEACTIONDataset(data.Dataset):
def __init__(self, cfg, mode='train'):
self.root_path = cfg['root_path']
self.mode = mode
self.training = mode == 'train'
self.window_size = cfg['window_size']
self.stride = cfg['stride']
data_name = cfg['data_name']
self.vids = json.load(open(cfg['video_list_path']))[data_name][mode + '_session_set']
self.num_classes = cfg['num_classes']
self.inputs = []
self._load_features(cfg)
self._init_features()
def _load_features(self, cfg):
self.annotation_type = cfg['annotation_type']
self.rgb_type = cfg['rgb_type']
self.flow_type = cfg['flow_type']
def _init_features(self, seed=0):
del self.inputs
gc.collect()
self.inputs = []
for vid in self.vids:
target = np.load(osp.join(self.root_path, self.annotation_type, vid + '.npy'))
if self.training:
seed = np.random.randint(self.stride)
for start, end in zip(range(seed, target.shape[0], self.stride),
range(seed + self.window_size, target.shape[0]+1, self.stride)):
self.inputs.append([
vid, start, end
])
else:
start = 0
end = target.shape[0]
self.inputs.append([
vid, start, end
])
def __getitem__(self, index):
vid, start, end = self.inputs[index]
rgb_input = np.load(osp.join(self.root_path, self.rgb_type, vid + '.npy'), mmap_mode='r')[start:end]
flow_input = np.load(osp.join(self.root_path, self.flow_type, vid + '.npy'), mmap_mode='r')[start:end]
target = np.load(osp.join(self.root_path, self.annotation_type, vid + '.npy'), mmap_mode='r')[start:end]
rgb_input = torch.tensor(rgb_input.astype(np.float32))
flow_input = torch.tensor(flow_input.astype(np.float32))
target = torch.tensor(target.astype(np.float32))
return rgb_input, flow_input, target
def __len__(self):
return len(self.inputs)

View File

@@ -0,0 +1,32 @@
__all__ = [
'build_dataset',
'build_data_loader',
]
import torch.utils.data as data
from utils import Registry
DATA_LAYERS = Registry()
def build_dataset(cfg):
data_layer = DATA_LAYERS[f'{cfg["data_name"]}']
return data_layer
def build_data_loader(cfg, mode):
data_layer = build_dataset(cfg)
data_loader = data.DataLoader(
dataset=data_layer(cfg, mode),
batch_size=cfg["batch_size"] if mode == 'train' else cfg["test_batch_size"],
shuffle=True if mode == 'train' else False,
num_workers=cfg["num_workers"],
pin_memory=False,
)
return data_loader

294
third_party/EgoSpeak/MiniROAD/main.py vendored Executable file
View File

@@ -0,0 +1,294 @@
import argparse
import os
import os.path as osp
import torch
import wandb
import yaml
from criterions import build_criterion
from model import build_model
from torch.utils.tensorboard import SummaryWriter
from trainer import build_eval, build_trainer
from utils import *
from utils import get_logger
from datasets import build_data_loader
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--config", type=str)
parser.add_argument("--eval", action="store_true")
parser.add_argument("--ckpt", type=str)
parser.add_argument("--amp", action="store_true")
parser.add_argument("--tensorboard", action="store_true")
parser.add_argument("--wandb", action="store_true")
parser.add_argument("--lr_scheduler", action="store_true")
parser.add_argument("--no_rgb", action="store_true")
parser.add_argument("--no_flow", action="store_true")
parser.add_argument(
"--save-as-cfg",
action="store_true",
help="Save outputs with config name in the output directory",
)
parser.add_argument("--debug", action="store_true")
parser.add_argument(
"--save-preds",
action="store_true",
help="Save predictions in the output directory",
)
# parser.add_argument('--model', type=str, default=None)
args = parser.parse_args()
# combine argparse and yaml
opt = yaml.load(open(args.config), Loader=yaml.FullLoader)
opt.update(vars(args))
cfg = opt
if "checkpoint" in cfg and cfg["checkpoint"] is not None and args.ckpt is None:
args.ckpt = cfg["checkpoint"]
print(f"checkpoint is given in the config, set args.ckpt to {args.ckpt}")
seed = 20 if "seed" not in cfg else cfg["seed"]
set_seed(seed)
device = "cuda" if torch.cuda.is_available() else "cpu"
annotation_type = os.path.basename(cfg["annotation_type"])
identifier = os.path.basename(cfg["config"])[:-5]
identifier += "_"
if cfg["no_flow"] == False:
identifier += "A"
if cfg["no_rgb"] == False:
identifier += "V"
if cfg["save_as_cfg"] == True:
output_dir = osp.join("output_cfg", identifier)
result_path = create_outdir(output_dir, overwrite=True)
else:
identifier = get_identifier(cfg)
annotation_type = os.path.basename(cfg["annotation_type"])
identifier += f"_{annotation_type}"
result_path = create_outdir(osp.join(cfg["output_path"], identifier))
logger = get_logger(result_path)
logger.info(f"Identifier: {identifier}")
logger.info(f"result_path: {result_path}")
logger.info(cfg)
testloader = build_data_loader(cfg, mode="test")
print("testloader loaded")
model = build_model(cfg, device)
evaluate = build_eval(cfg)
trainloader = build_data_loader(cfg, mode="train")
print("trainloader loaded")
# evaluation
if args.eval == True and cfg["ckpt"] == None:
print("Automatically find the best checkpoint")
ckpt_root = os.path.join("output_cfg", identifier, "ckpts")
ckpt_dirs = os.listdir(ckpt_root)
ckpt_path = os.path.join(ckpt_root, "best.pth")
if os.path.exists(ckpt_path):
args.ckpt = ckpt_path
print(f"args.ckpt set to {args.ckpt}")
else:
ckpt_path = os.path.join(ckpt_root, "pred", "best.pth")
if os.path.exists(ckpt_path):
args.ckpt = ckpt_path
print(f"args.ckpt set to {args.ckpt}")
print("Warninig) no ckpt_path found, there can be multiple ckpts")
print(f"ckpt_dirs: {ckpt_dirs}")
pred_dir_number = 0
ckpt_dirs.sort(reverse=True)
if args.ckpt == None:
for ckpt_dir in ckpt_dirs:
ckpt_path = os.path.join(ckpt_root, ckpt_dir, "best.pth")
if os.path.exists(ckpt_path):
args.ckpt = ckpt_path
print(f"Found ckpt, args.ckpt was None, set to {args.ckpt}")
break
if args.ckpt == None:
raise ValueError("No checkpoint found, args.ckpt is None")
# load checkpoint if args.ckpt is given
if args.eval == True:
if args.ckpt == None:
raise ValueError("Please specify the checkpoint path")
checkpoint = torch.load(args.ckpt)
model.load_state_dict(checkpoint)
elif args.eval == False and args.ckpt != None:
checkpoint = torch.load(args.ckpt)
print(f"Load checkpoint from {args.ckpt}")
model_dict = model.state_dict()
unnecessary_keys = []
matched_ckpt = {}
# Filter out unnecessary keys
for k, v in checkpoint.items():
if not (k in model_dict and model_dict[k].shape == checkpoint[k].shape):
unnecessary_keys.append(k)
elif (
"except_classifier" in cfg
and cfg["except_classifier"] == True
and "classification" in k
):
print(f"Unnecessary key: {k} is classifier")
unnecessary_keys.append(k)
else:
matched_ckpt[k] = v
# Overwrite entries in the existing state dict
model_dict.update(matched_ckpt)
# Load the new state dict
model.load_state_dict(model_dict)
print(f"Unnecessary keys: {unnecessary_keys}")
if cfg["wandb"] == True:
# args.tensorboard = True
print("wandb init")
os.environ["WANDB__SERVICE_WAIT"] = "3000"
wandb.init(
project="egospeak",
tags=[cfg["model"], cfg["data_name"], annotation_type],
config=cfg,
name=os.path.basename(result_path),
)
if args.eval == True or args.debug == True:
if args.eval == True and args.ckpt == None:
raise ValueError("Please specify the checkpoint path")
logger.info(f'Dataset: {cfg["data_name"]}, Model: {cfg["model"]}')
logger.info(f"Result path:{result_path}")
for phase in ["test"]:
if phase == "train":
raise NotImplementedError("evaluation on train split is not implemented")
dataloader = trainloader
elif phase == "test":
dataloader = testloader
mAP, mcAP, preds = evaluate(
model, dataloader, logger, result_path=result_path
)
pred_path = osp.join(result_path, "eval")
os.makedirs(pred_path, exist_ok=True)
if args.save_preds == True:
print(f"pred_path: {pred_path}")
for k, v in preds.items():
np.save(osp.join(pred_path, f"{k}.npy"), v)
logger.info(f'{phase}) {cfg["task"]} result: {mAP*100:.2f} mAP')
logger.info(f'{phase}) {cfg["task"]} result: {mcAP*100:.2f} mcAP')
exit()
criterion = build_criterion(cfg, device)
train_one_epoch = build_trainer(cfg)
optim = torch.optim.AdamW if cfg["optimizer"] == "AdamW" else torch.optim.Adam
optimizer = optim(
[{"params": model.parameters(), "initial_lr": cfg["lr"]}],
lr=cfg["lr"],
weight_decay=cfg["weight_decay"],
)
scheduler = (
build_lr_scheduler(cfg, optimizer, len(trainloader))
if args.lr_scheduler
else None
)
writer = SummaryWriter(osp.join(result_path, "runs")) if args.tensorboard else None
scaler = torch.cuda.amp.GradScaler() if args.amp else None
total_params = sum(p.numel() for p in model.parameters())
logger.info(f'Dataset: {cfg["data_name"]}, Model: {cfg["model"]}')
logger.info(
f'lr:{cfg["lr"]} | Weight Decay:{cfg["weight_decay"]} | Window Size:{cfg["window_size"]} | Batch Size:{cfg["batch_size"]}'
)
logger.info(
f'Total epoch:{cfg["num_epoch"]} | Total Params:{total_params/1e6:.1f} M | Optimizer: {cfg["optimizer"]}'
)
logger.info(f"Output Path:{result_path}")
best_mAP, best_epoch = 0, 0
best_cAP, best_cAP_epoch = 0, 0
pred_save_path = osp.join(result_path, "ckpts", "pred")
print(f"pred_save_path: {pred_save_path}")
# if you want to overwrite, change the code
os.makedirs(pred_save_path, exist_ok=True)
for epoch in range(1, cfg["num_epoch"] + 1):
epoch_loss = train_one_epoch(
trainloader,
model,
criterion,
optimizer,
scaler,
epoch,
cfg["wandb"],
writer,
scheduler=scheduler,
)
trainloader.dataset._init_features()
mAP, cAP, preds = evaluate(
model, testloader, logger, criterion, cfg["wandb"], epoch, writer
)
if mAP > best_mAP:
best_mAP = mAP
best_epoch = epoch
torch.save(model.state_dict(), osp.join(pred_save_path, "best.pth"))
if args.save_preds == True:
for k, v in preds.items():
np.save(osp.join(pred_save_path, f"{k}.npy"), v)
if cAP > best_cAP:
best_cAP = cAP
best_cAP_epoch = epoch
torch.save(model.state_dict(), osp.join(pred_save_path, "best_cAP.pth"))
logger.info(
f'Epoch {epoch} mAP: {mAP*100:.2f} | Best mAP: {best_mAP*100:.2f} at epoch {best_epoch}, iter {epoch*cfg["batch_size"]*len(trainloader)} | train_loss: {epoch_loss/len(trainloader):.4f}, lr: {optimizer.param_groups[0]["lr"]:.7f}'
)
logger.info(
f'Epoch {epoch} cAP: {cAP*100:.2f} | Best cAP: {best_cAP*100:.2f} at epoch {best_epoch}, iter {epoch*cfg["batch_size"]*len(trainloader)} | train_loss: {epoch_loss/len(trainloader):.4f}, lr: {optimizer.param_groups[0]["lr"]:.7f}'
)
if cfg["wandb"] == True:
logger.info("Wandb logging")
wandb.log(
{
"Epoch": epoch,
"Train Loss (Epoch)": epoch_loss / len(trainloader),
"Learning Rate": optimizer.param_groups[0]["lr"],
"anticipation average mAP": mAP,
"anticipation average cAP": cAP,
}
)
# os.rename(osp.join(result_path, 'ckpts', 'best.pth'), osp.join(result_path, 'ckpts', f'best_{best_mAP*100:.2f}.pth'))
# save the training result and config as txt file
with open(osp.join(result_path, "result.txt"), "w") as f:
f.write(f"Best mAP: {best_mAP*100:.2f} at epoch {best_epoch}\n")
f.write(f"Best cAP: {best_cAP*100:.2f} at epoch {best_cAP_epoch}\n")
f.write(f"Config: {cfg}")
wandb.finish() if args.wandb else None

View File

@@ -0,0 +1,4 @@
from .mamba import OADMamba
from .model_builder import build_model
from .rnn import MROAD
from .transformer_models import ViTEnc

View File

@@ -0,0 +1 @@
from .mamba import OADMamba, OADMambaA

View File

@@ -0,0 +1,172 @@
import sys
import torch
import torch.nn as nn
import torch.nn.functional as F
from configs.feature_sizes import FEATURE_SIZES
from mamba_ssm import Mamba
# from mambapy.mamba import Mamba, MambaConfig
from model.model_builder import META_ARCHITECTURES
from utils.util import parse_flow_type, parse_rgb_type
@META_ARCHITECTURES.register("Mamba")
class OADMamba(nn.Module):
def __init__(self, cfg):
super(OADMamba, self).__init__()
self.use_flow = not cfg['no_flow']
self.use_rgb = not cfg['no_rgb']
self.input_dim = 0
rgb_type = parse_rgb_type(cfg['rgb_type'])
flow_type = parse_flow_type(cfg['flow_type'])
if self.use_rgb:
self.input_dim += FEATURE_SIZES[rgb_type]
if self.use_flow:
self.input_dim += FEATURE_SIZES[flow_type]
self.hidden_dim = cfg['hidden_dim']
self.num_layers = cfg['num_layers']
# raise NotImplementedError("The following code is not implemented yet")
self.out_dim = cfg['num_classes']
self.window_size = cfg['window_size']
self.relu = nn.ReLU()
self.embedding_dim = cfg['embedding_dim']
self.d_state = cfg['d_state']
self.d_conv = cfg['d_conv']
self.expand = cfg['expand']
# self.mamba_config = MambaConfig(d_model=self.embedding_dim, n_layers=self.num_layers)
# self.mamba = Mamba(self.mamba_config)
self.mamba = Mamba(
d_model=self.embedding_dim, # Model dimension d_model
d_state=self.d_state, # SSM state expansion factor
d_conv=self.d_conv, # Local convolution width
expand=self.expand, # Block expansion factor
)
# self.gru = nn.GRU(self.embedding_dim, self.hidden_dim, self.num_layers, batch_first=True)
self.layer1 = nn.Sequential(
nn.Linear(self.input_dim, self.embedding_dim),
nn.LayerNorm(self.embedding_dim),
nn.ReLU(),
nn.Dropout(p=cfg['dropout']),
)
self.f_classification = nn.Sequential(
nn.Linear(self.hidden_dim, self.out_dim)
)
# self.h0 = torch.nn.Parameter(torch.zeros(1, 1, self.hidden_dim))
self.h0 = torch.zeros(self.num_layers, 1, self.hidden_dim)
def forward(self, rgb_input, flow_input):
x = torch.empty(0, device='cuda:0')
if self.use_rgb:
x = torch.cat((x, rgb_input), dim=2) # Concatenating along the channel dimension
if self.use_flow:
x = torch.cat((x, flow_input), dim=2)
x = self.layer1(x)
ht = self.mamba(x)
logits = self.f_classification(ht)
out_dict = {}
if self.training:
out_dict['logits'] = logits
else:
pred_scores = F.softmax(logits, dim=-1)
out_dict['logits'] = pred_scores
return out_dict
@META_ARCHITECTURES.register("MambaA")
class OADMambaA(nn.Module):
def __init__(self, cfg):
super(OADMambaA, self).__init__()
self.use_flow = not cfg['no_flow']
self.use_rgb = not cfg['no_rgb']
self.input_dim = 0
if self.use_rgb:
self.input_dim += FEATURE_SIZES[cfg['rgb_type']]
if self.use_flow:
self.input_dim += FEATURE_SIZES[cfg['flow_type']]
self.hidden_dim = cfg['hidden_dim']
self.num_layers = cfg['num_layers']
self.anticipation_length = cfg["anticipation_length"]
self.out_dim = cfg['num_classes']
self.embedding_dim = cfg['embedding_dim']
self.d_state = cfg['d_state'] if 'd_state' in cfg else 16
self.d_conv = cfg['d_conv'] if 'd_conv' in cfg else 4
self.expand = cfg['expand'] if 'expand' in cfg else 2
self.layer1 = nn.Sequential(
nn.Linear(self.input_dim, self.embedding_dim),
nn.LayerNorm(self.embedding_dim),
nn.ReLU(),
nn.Dropout(p=cfg['dropout'])
)
self.actionness = cfg['actionness']
if self.actionness:
self.f_actionness = nn.Sequential(
nn.Linear(self.hidden_dim, 1),
)
self.relu = nn.ReLU()
# self.mamba_config = MambaConfig(d_model=self.embedding_dim, n_layers=self.num_layers)
# self.mamba = Mamba(self.mamba_config)
self.mamba = Mamba(
d_model=self.embedding_dim, # Model dimension d_model
d_state=self.d_state, # SSM state expansion factor
d_conv=self.d_conv, # Local convolution width
expand=self.expand, # Block expansion facto
)
# self.gru = nn.GRU(self.embedding_dim, self.hidden_dim, self.num_layers, batch_first=True)
self.f_classification = nn.Sequential(
nn.Linear(self.embedding_dim, self.out_dim)
)
self.anticipation_layer = nn.Sequential(
nn.Linear(self.embedding_dim, self.anticipation_length*self.embedding_dim),
)
self.cfg = cfg
def forward(self, rgb_input, flow_input):
x = torch.empty(0, device='cuda:0')
if self.use_rgb:
# [64, 128, 2048]
x = torch.cat((x, rgb_input), dim=2) # Concatenating along the channel dimension
if self.use_flow:
# [64, 128, 5120]
x = torch.cat((x, flow_input), dim=2)
# audio channel is too huge to directly concat...
# to c
# B, L, D: mamba
B, S, feat_dim = x.shape # batch, length, dim
#
x = self.layer1(x)
ht = self.mamba(x)
# h0 = torch.zeros(1, B, self.hidden_dim).to(x.device)
# ht, _ = self.gru(x, h0)
logits = self.f_classification(self.relu(ht))
anticipation_ht = self.anticipation_layer(self.relu(ht)).view(B, S, self.anticipation_length, self.embedding_dim)
anticipation_logits = self.f_classification(self.relu(anticipation_ht))
out_dict = {}
if self.training:
out_dict['logits'] = logits
out_dict['anticipation_logits'] = anticipation_logits
else:
pred_scores = F.softmax(logits, dim=-1)
pred_anticipation_scores = F.softmax(anticipation_logits, dim=-1)
out_dict['logits'] = pred_scores
out_dict['anticipation_logits'] = pred_anticipation_scores
return out_dict

View File

@@ -0,0 +1,9 @@
__all__ = ['build_model']
from utils import Registry
META_ARCHITECTURES = Registry()
def build_model(cfg, device=None):
model = META_ARCHITECTURES[cfg["model"]](cfg)
return model.to(device)

View File

@@ -0,0 +1 @@
from .rnn import MROAD, MROADA

153
third_party/EgoSpeak/MiniROAD/model/rnn/rnn.py vendored Executable file
View File

@@ -0,0 +1,153 @@
import sys
import torch
import torch.nn as nn
import torch.nn.functional as F
from configs.feature_sizes import FEATURE_SIZES
from model.model_builder import META_ARCHITECTURES
from utils.util import parse_flow_type, parse_rgb_type
@META_ARCHITECTURES.register("MiniROAD")
class MROAD(nn.Module):
def __init__(self, cfg):
super(MROAD, self).__init__()
self.use_flow = not cfg['no_flow']
self.use_rgb = not cfg['no_rgb']
self.input_dim = 0
rgb_type = parse_rgb_type(cfg['rgb_type'])
flow_type = parse_flow_type(cfg['flow_type'])
if self.use_rgb:
self.input_dim += FEATURE_SIZES[rgb_type]
if self.use_flow:
self.input_dim += FEATURE_SIZES[flow_type]
self.hidden_dim = cfg['hidden_dim']
self.num_layers = cfg['num_layers']
self.out_dim = cfg['num_classes']
self.window_size = cfg['window_size']
self.relu = nn.ReLU()
self.embedding_dim = cfg['embedding_dim']
self.gru = nn.GRU(self.embedding_dim, self.hidden_dim, self.num_layers, batch_first=True)
self.layer1 = nn.Sequential(
nn.Linear(self.input_dim, self.embedding_dim),
nn.LayerNorm(self.embedding_dim),
nn.ReLU(),
nn.Dropout(p=cfg['dropout']),
)
self.f_classification = nn.Sequential(
nn.Linear(self.hidden_dim, self.out_dim)
)
# self.h0 = torch.nn.Parameter(torch.zeros(1, 1, self.hidden_dim))
self.h0 = torch.zeros(self.num_layers, 1, self.hidden_dim)
def forward(self, rgb_input, flow_input):
x = torch.empty(0, device='cuda:0')
if self.use_rgb:
x = torch.cat((x, rgb_input), dim=2) # Concatenating along the channel dimension
if self.use_flow:
x = torch.cat((x, flow_input), dim=2)
# if self.use_rgb and self.use_flow:
# x = torch.cat((rgb_input, flow_input), 2)
# elif self.use_rgb:
# x = rgb_input
# elif self.use_flow:
# x = flow_input
x = self.layer1(x)
B, _, _ = x.shape
h0 = self.h0.expand(-1, B, -1).to(x.device)
ht, _ = self.gru(x, h0)
ht = self.relu(ht)
# ht = self.relu(ht + x)
logits = self.f_classification(ht)
out_dict = {}
if self.training:
out_dict['logits'] = logits
else:
pred_scores = F.softmax(logits, dim=-1)
out_dict['logits'] = pred_scores
return out_dict
@META_ARCHITECTURES.register("MiniROADA")
class MROADA(nn.Module):
def __init__(self, cfg):
super(MROADA, self).__init__()
self.use_flow = not cfg['no_flow']
self.use_rgb = not cfg['no_rgb']
self.input_dim = 0
if self.use_rgb:
self.input_dim += FEATURE_SIZES[cfg['rgb_type']]
if self.use_flow:
self.input_dim += FEATURE_SIZES[cfg['flow_type']]
self.embedding_dim = cfg['embedding_dim']
self.hidden_dim = cfg['hidden_dim']
self.num_layers = cfg['num_layers']
self.anticipation_length = cfg["anticipation_length"]
self.out_dim = cfg['num_classes']
self.layer1 = nn.Sequential(
nn.Linear(self.input_dim, self.embedding_dim),
nn.LayerNorm(self.embedding_dim),
nn.ReLU(),
nn.Dropout(p=cfg['dropout'])
)
self.actionness = cfg['actionness']
if self.actionness:
self.f_actionness = nn.Sequential(
nn.Linear(self.hidden_dim, 1),
)
self.relu = nn.ReLU()
self.gru = nn.GRU(self.embedding_dim, self.hidden_dim, self.num_layers, batch_first=True)
self.f_classification = nn.Sequential(
nn.Linear(self.hidden_dim, self.out_dim)
)
self.anticipation_layer = nn.Sequential(
nn.Linear(self.hidden_dim, self.anticipation_length*self.hidden_dim),
)
self.cfg = cfg
def forward(self, rgb_input, flow_input):
x = torch.empty(0, device='cuda:0')
if self.use_rgb:
# [64, 128, 2048]
x = torch.cat((x, rgb_input), dim=2) # Concatenating along the channel dimension
if self.use_flow:
# [64, 128, 5120]
x = torch.cat((x, flow_input), dim=2)
# audio channel is too huge to directly concat...
# to c
B, S, feat_dim = x.shape # batch, length, dim
#
x = self.layer1(x)
h0 = torch.zeros(1, B, self.hidden_dim).to(x.device)
ht, _ = self.gru(x, h0)
ht = self.relu(ht)
logits = self.f_classification(self.relu(ht))
anticipation_ht = self.anticipation_layer(self.relu(ht)).view(B, S, self.anticipation_length, self.hidden_dim)
anticipation_logits = self.f_classification(self.relu(anticipation_ht))
out_dict = {}
if self.training:
out_dict['logits'] = logits
out_dict['anticipation_logits'] = anticipation_logits
else:
pred_scores = F.softmax(logits, dim=-1)
pred_anticipation_scores = F.softmax(anticipation_logits, dim=-1)
out_dict['logits'] = pred_scores
out_dict['anticipation_logits'] = pred_anticipation_scores
return out_dict

View File

@@ -0,0 +1,155 @@
import math
import torch
import torch.nn as nn
import torch.nn.functional as F
class SelfAttention(nn.Module):
def __init__(
self, dim, heads=8, qkv_bias=False, qk_scale=None, dropout_rate=0.0
):
super().__init__()
self.num_heads = heads
head_dim = dim // heads
self.scale = qk_scale or head_dim ** -0.5
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
self.attn_drop = nn.Dropout(dropout_rate)
self.proj = nn.Linear(dim, dim)
self.proj_drop = nn.Dropout(dropout_rate)
def forward(self, x):
B, N, C = x.shape
qkv = (
self.qkv(x)
.reshape(B, N, 3, self.num_heads, C // self.num_heads)
.permute(2, 0, 3, 1, 4)
)
q, k, v = (
qkv[0],
qkv[1],
qkv[2],
) # make torchscript happy (cannot use tensor as tuple)
attn = (q @ k.transpose(-2, -1)) * self.scale
attn = attn.softmax(dim=-1)
attn = self.attn_drop(attn)
x = (attn @ v).transpose(1, 2).reshape(B, N, C)
x = self.proj(x)
x = self.proj_drop(x)
return x
class AxialAttention(nn.Module):
def __init__(
self,
in_planes,
out_planes,
groups=8,
kernel_size=56,
stride=1,
bias=False,
width=False,
):
assert (in_planes % groups == 0) and (out_planes % groups == 0)
super(AxialAttention, self).__init__()
self.in_planes = in_planes
self.out_planes = out_planes
self.groups = groups
self.group_planes = out_planes // groups
self.kernel_size = kernel_size
self.stride = stride
self.bias = bias
self.width = width
# Multi-head self attention
self.qkv_transform = nn.Conv1d(
in_planes,
out_planes * 2,
kernel_size=1,
stride=1,
padding=0,
bias=False,
)
self.bn_qkv = nn.BatchNorm1d(out_planes * 2)
self.bn_similarity = nn.BatchNorm2d(groups * 3)
self.bn_output = nn.BatchNorm1d(out_planes * 2)
# Position embedding
self.relative = nn.Parameter(
torch.randn(self.group_planes * 2, kernel_size * 2 - 1),
requires_grad=True,
)
query_index = torch.arange(kernel_size).unsqueeze(0)
key_index = torch.arange(kernel_size).unsqueeze(1)
relative_index = key_index - query_index + kernel_size - 1
self.register_buffer('flatten_index', relative_index.view(-1))
if stride > 1:
self.pooling = nn.AvgPool2d(stride, stride=stride)
self.reset_parameters()
def forward(self, x):
if self.width:
x = x.permute(0, 2, 1, 3)
else:
x = x.permute(0, 3, 1, 2) # N, W, C, H
N, W, C, H = x.shape
x = x.contiguous().view(N * W, C, H)
# Transformations
qkv = self.bn_qkv(self.qkv_transform(x))
q, k, v = torch.split(
qkv.reshape(N * W, self.groups, self.group_planes * 2, H),
[self.group_planes // 2, self.group_planes // 2, self.group_planes],
dim=2,
)
# Calculate position embedding
all_embeddings = torch.index_select(
self.relative, 1, self.flatten_index
).view(self.group_planes * 2, self.kernel_size, self.kernel_size)
q_embedding, k_embedding, v_embedding = torch.split(
all_embeddings,
[self.group_planes // 2, self.group_planes // 2, self.group_planes],
dim=0,
)
qr = torch.einsum('bgci,cij->bgij', q, q_embedding)
kr = torch.einsum('bgci,cij->bgij', k, k_embedding).transpose(2, 3)
qk = torch.einsum('bgci, bgcj->bgij', q, k)
stacked_similarity = torch.cat([qk, qr, kr], dim=1)
stacked_similarity = (
self.bn_similarity(stacked_similarity)
.view(N * W, 3, self.groups, H, H)
.sum(dim=1)
)
# (N, groups, H, H, W)
similarity = F.softmax(stacked_similarity, dim=3)
sv = torch.einsum('bgij,bgcj->bgci', similarity, v)
sve = torch.einsum('bgij,cij->bgci', similarity, v_embedding)
stacked_output = torch.cat([sv, sve], dim=-1).view(
N * W, self.out_planes * 2, H
)
output = (
self.bn_output(stacked_output)
.view(N, W, self.out_planes, 2, H)
.sum(dim=-2)
)
if self.width:
output = output.permute(0, 2, 1, 3)
else:
output = output.permute(0, 2, 3, 1)
if self.stride > 1:
output = self.pooling(output)
return output
def reset_parameters(self):
self.qkv_transform.weight.data.normal_(
0, math.sqrt(1.0 / self.in_planes)
)
nn.init.normal_(self.relative, 0.0, math.sqrt(1.0 / self.group_planes))

View File

@@ -0,0 +1,244 @@
import torch
import torch.nn as nn
from .Attention import AxialAttention
def conv1x1(in_planes, out_planes, stride=1):
"""1x1 convolution"""
return nn.Conv2d(
in_planes, out_planes, kernel_size=1, stride=stride, bias=False
)
class AxialBlock(nn.Module):
expansion = 2
def __init__(
self,
inplanes,
planes,
stride=1,
downsample=None,
groups=1,
base_width=64,
dilation=1,
norm_layer=None,
kernel_size=56,
):
super(AxialBlock, self).__init__()
if norm_layer is None:
norm_layer = nn.BatchNorm2d
width = int(planes * (base_width / 64.0))
# Both self.conv2 and self.downsample layers downsample the input when stride != 1
self.conv_down = conv1x1(inplanes, width)
self.bn1 = norm_layer(width)
self.hight_block = AxialAttention(
width, width, groups=groups, kernel_size=kernel_size
)
self.width_block = AxialAttention(
width,
width,
groups=groups,
kernel_size=kernel_size,
stride=stride,
width=True,
)
self.conv_up = conv1x1(width, planes * self.expansion)
self.bn2 = norm_layer(planes * self.expansion)
self.relu = nn.ReLU(inplace=True)
self.downsample = downsample
self.stride = stride
def forward(self, x):
identity = x
out = self.conv_down(x)
out = self.bn1(out)
out = self.relu(out)
out = self.hight_block(out)
out = self.width_block(out)
out = self.relu(out)
out = self.conv_up(out)
out = self.bn2(out)
if self.downsample is not None:
identity = self.downsample(x)
out += identity
out = self.relu(out)
return out
class AxialAttentionNet(nn.Module):
def __init__(
self,
layers,
num_classes=1000,
zero_init_residual=True,
groups=8,
width_per_group=64,
replace_stride_with_dilation=None,
norm_layer=None,
s=0.5,
):
super(AxialAttentionNet, self).__init__()
block = AxialBlock
if norm_layer is None:
norm_layer = nn.BatchNorm2d
self._norm_layer = norm_layer
self.inplanes = int(64 * s)
self.dilation = 1
if replace_stride_with_dilation is None:
# each element in the tuple indicates if we should replace
# the 2x2 stride with a dilated convolution instead
replace_stride_with_dilation = [False, False, False]
if len(replace_stride_with_dilation) != 3:
raise ValueError(
"replace_stride_with_dilation should be None "
"or a 3-element tuple, got {}".format(
replace_stride_with_dilation
)
)
self.groups = groups
self.base_width = width_per_group
self.conv1 = nn.Conv2d(
3, self.inplanes, kernel_size=7, stride=2, padding=3, bias=False
)
self.bn1 = norm_layer(self.inplanes)
self.relu = nn.ReLU(inplace=True)
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.layer1 = self._make_layer(
block, int(128 * s), layers[0], kernel_size=56
)
self.layer2 = self._make_layer(
block,
int(256 * s),
layers[1],
stride=2,
kernel_size=56,
dilate=replace_stride_with_dilation[0],
)
self.layer3 = self._make_layer(
block,
int(512 * s),
layers[2],
stride=2,
kernel_size=28,
dilate=replace_stride_with_dilation[1],
)
self.layer4 = self._make_layer(
block,
int(1024 * s),
layers[3],
stride=2,
kernel_size=14,
dilate=replace_stride_with_dilation[2],
)
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
self.fc = nn.Linear(int(1024 * block.expansion * s), num_classes)
for m in self.modules():
if isinstance(m, (nn.Conv2d, nn.Conv1d)):
if isinstance(m, nn.Conv1d):
pass
else:
nn.init.kaiming_normal_(
m.weight, mode='fan_out', nonlinearity='relu'
)
elif isinstance(m, (nn.BatchNorm2d, nn.BatchNorm1d, nn.GroupNorm)):
nn.init.constant_(m.weight, 1)
nn.init.constant_(m.bias, 0)
# Zero-initialize the last BN in each residual branch,
# so that the residual branch starts with zeros, and each residual block behaves like an identity.
# This improves the model by 0.2~0.3% according to https://arxiv.org/abs/1706.02677
if zero_init_residual:
for m in self.modules():
if isinstance(m, AxialBlock):
nn.init.constant_(m.bn2.weight, 0)
def _make_layer(
self, block, planes, blocks, kernel_size=56, stride=1, dilate=False
):
norm_layer = self._norm_layer
downsample = None
previous_dilation = self.dilation
if dilate:
self.dilation *= stride
stride = 1
if stride != 1 or self.inplanes != planes * block.expansion:
downsample = nn.Sequential(
conv1x1(self.inplanes, planes * block.expansion, stride),
norm_layer(planes * block.expansion),
)
layers = []
layers.append(
block(
self.inplanes,
planes,
stride,
downsample,
groups=self.groups,
base_width=self.base_width,
dilation=previous_dilation,
norm_layer=norm_layer,
kernel_size=kernel_size,
)
)
self.inplanes = planes * block.expansion
if stride != 1:
kernel_size = kernel_size // 2
for _ in range(1, blocks):
layers.append(
block(
self.inplanes,
planes,
groups=self.groups,
base_width=self.base_width,
dilation=self.dilation,
norm_layer=norm_layer,
kernel_size=kernel_size,
)
)
return nn.Sequential(*layers)
def _forward_impl(self, x, include_conv5=False, include_top=False):
# See note [TorchScript super()]
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.maxpool(x)
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
if include_conv5:
x = self.layer4(x)
if include_top:
x = self.avgpool(x)
x = torch.flatten(x, 1)
x = self.fc(x)
return x
def forward(self, x, include_conv5=False, include_top=False):
return self._forward_impl(x, include_conv5, include_top)
if __name__ == "__main__":
model = AxialAttentionNet([1, 2, 4, 1], s=0.5)
# model = AxialAttentionNet([3, 4, 6, 3], s=0.25)
# print(model)
x = torch.randn([8, 3, 224, 224])
print(model(x).shape)
print(model(x, include_conv5=True).shape)
print(model(x, include_conv5=True, include_top=True).shape)

View File

@@ -0,0 +1,138 @@
import torch
import torch.nn as nn
import torch.nn.functional as F
from collections import OrderedDict
class StdConv2d(nn.Conv2d):
def forward(self, x):
w = self.weight
v, m = torch.var_mean(w, dim=[1, 2, 3], keepdim=True, unbiased=False)
w = (w - m) / torch.sqrt(v + 1e-10)
return F.conv2d(
x,
w,
self.bias,
self.stride,
self.padding,
self.dilation,
self.groups,
)
def conv3x3(in_planes, out_planes, stride=1, groups=1, dilation=1):
"""3x3 convolution with padding"""
return StdConv2d(
in_planes,
out_planes,
kernel_size=3,
stride=stride,
padding=dilation,
groups=groups,
bias=False,
dilation=dilation,
)
def conv1x1(in_planes, out_planes, stride=1):
"""1x1 convolution"""
return StdConv2d(
in_planes, out_planes, kernel_size=1, stride=stride, bias=False
)
class PreActBottleneck(nn.Module):
"""Pre-activation (v2) bottleneck block.
Follows the implementation of "Identity Mappings in Deep Residual Networks":
https://github.com/KaimingHe/resnet-1k-layers/blob/master/resnet-pre-act.lua
Except it puts the stride on 3x3 conv when available.
"""
def __init__(self, in_planes, out_planes=None, mid_planes=None, stride=1):
super(PreActBottleneck, self).__init__()
out_planes = out_planes or in_planes
mid_planes = mid_planes or out_planes // 4
self.gn1 = nn.GroupNorm(32, in_planes)
self.conv1 = conv1x1(in_planes, mid_planes)
self.gn2 = nn.GroupNorm(32, mid_planes)
self.conv2 = conv3x3(mid_planes, mid_planes, stride)
self.gn3 = nn.GroupNorm(32, mid_planes)
self.conv3 = conv1x1(mid_planes, out_planes)
self.relu = nn.ReLU(inplace=True)
if stride != 1 or in_planes != out_planes:
# Projection also with pre-activation according to paper.
self.downsample = conv1x1(in_planes, out_planes, stride)
def forward(self, x):
out = self.relu(self.gn1(x))
# Residual branch
residual = x
if hasattr(self, 'downsample'):
residual = self.downsample(out)
# Unit's branch
out = self.conv1(out)
out = self.conv2(self.relu(self.gn2(out)))
out = self.conv3(self.relu(self.gn3(out)))
return out + residual
class ResNetV2Model(nn.Module):
"""Implementation of Pre-activation (v2) ResNet mode."""
def __init__(self, block_units, width_factor, head_size=21843):
super(ResNetV2Model, self).__init__()
wf = width_factor # shortcut 'cause we'll use it a lot.
# The following will be unreadable if we split lines.
# pylint: disable=line-too-long
self.conv1 = nn.Sequential(OrderedDict([
('conv', StdConv2d(3, 64*wf, kernel_size=7, stride=2, padding=3, bias=False)),
('pad', nn.ConstantPad2d(1, 0)),
('pool', nn.MaxPool2d(kernel_size=3, stride=2, padding=0)),
]))
self.conv2 = nn.Sequential(OrderedDict(
[('unit01', PreActBottleneck(in_planes=64*wf, out_planes=256*wf, mid_planes=64*wf))] +
[(f'unit{i:02d}', PreActBottleneck(in_planes=256*wf, out_planes=256*wf, mid_planes=64*wf)) for i in range(2, block_units[0] + 1)],
))
self.conv3 = nn.Sequential(OrderedDict(
[('unit01', PreActBottleneck(in_planes=256*wf, out_planes=512*wf, mid_planes=128*wf, stride=2))] +
[(f'unit{i:02d}', PreActBottleneck(in_planes=512*wf, out_planes=512*wf, mid_planes=128*wf)) for i in range(2, block_units[1] + 1)],
))
self.conv4 = nn.Sequential(OrderedDict(
[('unit01', PreActBottleneck(in_planes=512*wf, out_planes=1024*wf, mid_planes=256*wf, stride=2))] +
[(f'unit{i:02d}', PreActBottleneck(in_planes=1024*wf, out_planes=1024*wf, mid_planes=256*wf)) for i in range(2, block_units[2] + 1)],
))
self.conv5 = nn.Sequential(OrderedDict(
[('unit01', PreActBottleneck(in_planes=1024*wf, out_planes=2048*wf, mid_planes=512*wf, stride=2))] +
[(f'unit{i:02d}', PreActBottleneck(in_planes=2048*wf, out_planes=2048*wf, mid_planes=512*wf)) for i in range(2, block_units[3] + 1)],
))
# pylint: enable=line-too-long
self.head = nn.Sequential(OrderedDict([
('gn', nn.GroupNorm(32, 2048*wf)),
('relu', nn.ReLU(inplace=True)),
('avg', nn.AdaptiveAvgPool2d(output_size=1)),
('conv', nn.Conv2d(2048*wf, head_size, kernel_size=1, bias=True)),
]))
def forward(self, x, include_conv5=False, include_top=False):
x = self.conv1(x)
x = self.conv2(x)
x = self.conv3(x)
x = self.conv4(x)
if include_conv5:
x = self.conv5(x)
if include_top:
x = self.head(x)
if include_top and include_conv5:
assert x.shape[-2:] == (1,1,)
return x[..., 0, 0]
return x

View File

@@ -0,0 +1,223 @@
import torch
import torch.nn as nn
import torch.nn.functional as F
from .BiT import ResNetV2Model
from .AxialNet import AxialAttentionNet
from .Transformer import TransformerModel
from .PositionalEncoding import (
FixedPositionalEncoding,
LearnedPositionalEncoding,
)
class HybridVisionTransformer(nn.Module):
def __init__(
self,
img_dim,
out_dim,
num_channels,
embedding_dim,
num_heads,
num_layers,
hidden_dim,
include_conv5,
dropout_rate,
positional_encoding_type,
backbone=None,
):
super(HybridVisionTransformer, self).__init__()
assert embedding_dim % num_heads == 0
self.embedding_dim = embedding_dim
self.num_heads = num_heads
self.out_dim = out_dim
self.num_channels = num_channels
self.include_conv5 = include_conv5
self.backbone = backbone
self.backbone_model, self.flatten_dim = self.configure_backbone()
self.projection_encoding = nn.Linear(self.flatten_dim, embedding_dim)
self.cls_token = nn.Parameter(torch.randn(1, 1, embedding_dim))
self.decoder_dim = int(img_dim / 16.0) ** 2
if self.include_conv5:
self.decoder_dim = int(img_dim / 32.0) ** 2
self.decoder_dim += 1 # for the cls token
if positional_encoding_type == "learned":
self.position_encoding = LearnedPositionalEncoding(
self.decoder_dim, self.embedding_dim, self.decoder_dim
)
elif positional_encoding_type == "fixed":
self.position_encoding = FixedPositionalEncoding(
self.embedding_dim,
)
self.transformer = TransformerModel(
embedding_dim, num_layers, num_heads, hidden_dim
)
self.mlp_head = nn.Sequential(
nn.Linear(embedding_dim, hidden_dim),
nn.GELU(),
nn.Linear(hidden_dim, out_dim),
)
self.to_cls_token = nn.Identity()
def forward(self, x):
# apply bit backbone
x = self.backbone_model(x, include_conv5=self.include_conv5)
x = x.view(x.size(0), -1, self.flatten_dim)
x = self.projection_encoding(x)
cls_tokens = self.cls_token.expand(x.shape[0], -1, -1)
x = torch.cat((cls_tokens, x), dim=1)
x = self.position_encoding(x)
# apply transformer
x = self.transformer(x)
x = self.to_cls_token(x[:, 0])
x = self.mlp_head(x)
x = F.log_softmax(x, dim=-1)
return x
def configure_backbone(self):
raise NotImplementedError("Method to be called in child class!!")
class ResNetHybridViT(HybridVisionTransformer):
def __init__(
self,
img_dim,
out_dim,
num_channels,
embedding_dim,
num_heads,
num_layers,
hidden_dim,
include_conv5=False,
dropout_rate=0.1,
positional_encoding_type="learned",
backbone='r50x1',
):
super(ResNetHybridViT, self).__init__(
img_dim=img_dim,
out_dim=out_dim,
num_channels=num_channels,
embedding_dim=embedding_dim,
num_heads=num_heads,
num_layers=num_layers,
hidden_dim=hidden_dim,
include_conv5=include_conv5,
dropout_rate=dropout_rate,
positional_encoding_type=positional_encoding_type,
backbone=backbone,
)
def configure_backbone(self):
"""
Current support offered for all BiT models
KNOWN_MODELS in https://github.com/google-research/big_transfer/blob/master/bit_pytorch/models.py
expects model name of style 'r{depth}x{width}'
where depth in [50, 101, 152]
where width in [1,2,3,4]
"""
backbone = self.backbone
out_dim = self.out_dim
splits = backbone.split('x')
model_name = splits[0]
width_factor = int(splits[1])
if model_name in ['r50', 'r101'] and width_factor in [2, 4]:
return ValueError(
"Invalid Configuration of models -- expect 50x1, 50x3, 101x1, 101x3"
)
elif model_name == 'r152' and width_factor in [1, 3]:
return ValueError(
"Invalid Configuration of models -- expect 152x2, 152x4"
)
block_units_dict = {
'r50': [3, 4, 6, 3],
'r101': [3, 4, 23, 3],
'r152': [3, 8, 36, 3],
}
block_units = block_units_dict.get(model_name, [3, 4, 6, 3])
model = ResNetV2Model(block_units, width_factor, head_size=out_dim)
if self.num_channels == 3:
flatten_dim = 1024 * width_factor
if self.include_conv5:
flatten_dim *= 2
return model, flatten_dim
class AxialNetHybridViT(HybridVisionTransformer):
def __init__(
self,
img_dim,
out_dim,
num_channels,
embedding_dim,
num_heads,
num_layers,
hidden_dim,
include_conv5=False,
dropout_rate=0.1,
positional_encoding_type="learned",
backbone='a50m',
):
super(AxialNetHybridViT, self).__init__(
img_dim=img_dim,
out_dim=out_dim,
num_channels=num_channels,
embedding_dim=embedding_dim,
num_heads=num_heads,
num_layers=num_layers,
hidden_dim=hidden_dim,
include_conv5=include_conv5,
dropout_rate=dropout_rate,
positional_encoding_type=positional_encoding_type,
backbone=backbone,
)
def configure_backbone(self):
"""
Current support offered for all BiT models
models from https://github.com/csrhddlam/axial-deeplab/blob/master/lib/models/axialnet.py
expects model name of style 'a{depth}{width}'
where depth in [26, 50, 101]
where width in [s, m, l]
"""
backbone = self.backbone
out_dim = self.out_dim
model_name = backbone[:3]
width = backbone[-1]
block_units_dict = {
'a26': [1, 2, 4, 1],
'a50': [3, 4, 6, 3],
'a101': [3, 4, 23, 3],
}
block_units = block_units_dict.get(model_name, [3, 4, 6, 3])
scale_factor_dict = {'s': 0.5, 'm': 0.75, 'l': 1.0}
scale_factor = scale_factor_dict.get(width, 0.75)
model = AxialAttentionNet(
block_units, s=scale_factor, num_classes=out_dim
)
if self.num_channels == 3:
flatten_dim = int(512 * float(scale_factor / 0.5))
if self.include_conv5:
flatten_dim *= 2
return model, flatten_dim

View File

@@ -0,0 +1,331 @@
import torch
import torch.nn as nn
import torch.nn.functional as F
from .decoder import Decoder, DecoderLayer
from .attn import FullAttention, ProbAttention, AttentionLayer
from .Transformer import TransformerModel
from ipdb import set_trace
from .PositionalEncoding import (
FixedPositionalEncoding,
LearnedPositionalEncoding,
)
__all__ = ['ViT_B16', 'ViT_B32', 'ViT_L16', 'ViT_L32', 'ViT_H14']
class VisionTransformer_v3(nn.Module):
def __init__(
self,
args,
img_dim,
patch_dim,
out_dim,
embedding_dim,
num_heads,
num_layers,
hidden_dim,
dropout_rate=0.0,
attn_dropout_rate=0.0,
use_representation=True,
conv_patch_representation=False,
positional_encoding_type="learned", with_camera=True, with_motion=True, num_channels=3072,
):
super(VisionTransformer_v3, self).__init__()
assert embedding_dim % num_heads == 0
assert img_dim % patch_dim == 0
self.with_camera = with_camera
self.with_motion = with_motion
self.embedding_dim = embedding_dim
self.num_heads = num_heads
self.patch_dim = patch_dim
# num_channels = img_dim
self.num_channels = num_channels
self.dropout_rate = dropout_rate
self.attn_dropout_rate = attn_dropout_rate
self.conv_patch_representation = conv_patch_representation
# self.num_patches = int((img_dim // patch_dim) ** 2)
self.num_patches = int(img_dim // patch_dim)
self.seq_length = self.num_patches + 1
self.flatten_dim = patch_dim * patch_dim * num_channels
self.cls_token = nn.Parameter(torch.zeros(1, 1, embedding_dim))
self.linear_encoding = nn.Linear(self.flatten_dim, embedding_dim)
if positional_encoding_type == "learned":
self.position_encoding = LearnedPositionalEncoding(
self.seq_length, self.embedding_dim, self.seq_length
)
elif positional_encoding_type == "fixed":
self.position_encoding = FixedPositionalEncoding(
self.embedding_dim,
)
print('position encoding :', positional_encoding_type)
self.pe_dropout = nn.Dropout(p=self.dropout_rate)
self.encoder = TransformerModel(
embedding_dim,
num_layers,
num_heads,
hidden_dim,
self.dropout_rate,
self.attn_dropout_rate,
)
self.pre_head_ln = nn.LayerNorm(embedding_dim)
d_model = args.decoder_embedding_dim
use_representation = False # False
if use_representation:
self.mlp_head = nn.Sequential(
nn.Linear(embedding_dim + d_model, hidden_dim//2),
# nn.Tanh(),
nn.ReLU(),
nn.Linear(hidden_dim//2, out_dim),
)
else:
self.mlp_head = nn.Linear(embedding_dim + d_model, out_dim)
if self.conv_patch_representation:
# self.conv_x = nn.Conv2d(
# self.num_channels,
# self.embedding_dim,
# kernel_size=(self.patch_dim, self.patch_dim),
# stride=(self.patch_dim, self.patch_dim),
# padding=self._get_padding(
# 'VALID', (self.patch_dim, self.patch_dim),
# ),
# )
self.conv_x = nn.Conv1d(
self.num_channels,
self.embedding_dim,
kernel_size=self.patch_dim,
stride=self.patch_dim,
padding=self._get_padding(
'VALID', (self.patch_dim),
),
)
else:
self.conv_x = None
self.to_cls_token = nn.Identity()
# Decoder
factor = 1 # 5
dropout = args.decoder_attn_dropout_rate
# d_model = args.decoder_embedding_dim
n_heads = args.decoder_num_heads
d_layers = args.decoder_layers
d_ff = args.decoder_embedding_dim_out # args.decoder_embedding_dim_out or 4*args.decoder_embedding_dim None
activation = 'gelu' # 'gelu'
self.decoder = Decoder(
[
DecoderLayer(
AttentionLayer(FullAttention(True, factor, attention_dropout=dropout), # True
d_model, n_heads), # ProbAttention FullAttention
AttentionLayer(FullAttention(False, factor, attention_dropout=dropout), # False
d_model, n_heads),
d_model,
d_ff,
dropout=dropout,
activation=activation,
)
for l in range(d_layers)
],
norm_layer=torch.nn.LayerNorm(d_model)
)
self.decoder_cls_token = nn.Parameter(torch.zeros(1, args.query_num, d_model))
if positional_encoding_type == "learned":
self.decoder_position_encoding = LearnedPositionalEncoding(
args.query_num, self.embedding_dim, args.query_num
)
elif positional_encoding_type == "fixed":
self.decoder_position_encoding = FixedPositionalEncoding(
self.embedding_dim,
)
print('position decoding :', positional_encoding_type)
self.classifier = nn.Linear(d_model, out_dim)
self.after_dropout = nn.Dropout(p=self.dropout_rate)
# self.merge_fc = nn.Linear(d_model, 1)
# self.merge_sigmoid = nn.Sigmoid()
def forward(self, sequence_input_rgb, sequence_input_flow):
if self.with_camera and self.with_motion:
x = torch.cat((sequence_input_rgb, sequence_input_flow), 2)
elif self.with_camera:
x = sequence_input_rgb
elif self.with_motion:
x = sequence_input_flow
x = self.linear_encoding(x)
cls_tokens = self.cls_token.expand(x.shape[0], -1, -1)
# x = torch.cat((cls_tokens, x), dim=1)
x = torch.cat((x, cls_tokens), dim=1)
x = self.position_encoding(x)
x = self.pe_dropout(x) # not delete
# apply transformer
x = self.encoder(x)
x = self.pre_head_ln(x) # [128, 33, 1024]
# x = self.after_dropout(x) # add
# decoder
decoder_cls_token = self.decoder_cls_token.expand(x.shape[0], -1, -1)
# decoder_cls_token = self.after_dropout(decoder_cls_token) # add
# decoder_cls_token = self.decoder_position_encoding(decoder_cls_token) # [128, 8, 1024]
dec = self.decoder(decoder_cls_token, x) # [128, 8, 1024]
dec = self.after_dropout(dec) # add
# merge_atte = self.merge_sigmoid(self.merge_fc(dec)) # [128, 8, 1]
# dec_for_token = (merge_atte*dec).sum(dim=1) # [128, 1024]
# dec_for_token = (merge_atte*dec).sum(dim=1)/(merge_atte.sum(dim=-2) + 0.0001)
dec_for_token = dec.mean(dim=1)
# dec_for_token = dec.max(dim=1)[0]
dec_cls_out = self.classifier(dec)
# set_trace()
# x = self.to_cls_token(x[:, 0])
x = torch.cat((self.to_cls_token(x[:, -1]), dec_for_token), dim=1)
x = self.mlp_head(x)
# x = F.log_softmax(x, dim=-1)
return x, dec_cls_out
def _get_padding(self, padding_type, kernel_size):
assert padding_type in ['SAME', 'VALID']
if padding_type == 'SAME':
_list = [(k - 1) // 2 for k in kernel_size]
return tuple(_list)
return tuple(0 for _ in kernel_size)
def ViT_B16(dataset='imagenet'):
if dataset == 'imagenet':
img_dim = 224
out_dim = 1000
patch_dim = 16
elif 'cifar' in dataset:
img_dim = 32
out_dim = 10
patch_dim = 4
return VisionTransformer(
img_dim=img_dim,
patch_dim=patch_dim,
out_dim=out_dim,
num_channels=3,
embedding_dim=768,
num_heads=12,
num_layers=12,
hidden_dim=3072,
dropout_rate=0.1,
attn_dropout_rate=0.0,
use_representation=False,
conv_patch_representation=False,
positional_encoding_type="learned",
)
def ViT_B32(dataset='imagenet'):
if dataset == 'imagenet':
img_dim = 224
out_dim = 1000
patch_dim = 32
elif 'cifar' in dataset:
img_dim = 32
out_dim = 10
patch_dim = 4
return VisionTransformer(
img_dim=img_dim,
patch_dim=patch_dim,
out_dim=out_dim,
num_channels=3,
embedding_dim=768,
num_heads=12,
num_layers=12,
hidden_dim=3072,
dropout_rate=0.1,
attn_dropout_rate=0.0,
use_representation=False,
conv_patch_representation=False,
positional_encoding_type="learned",
)
def ViT_L16(dataset='imagenet'):
if dataset == 'imagenet':
img_dim = 224
out_dim = 1000
patch_dim = 16
elif 'cifar' in dataset:
img_dim = 32
out_dim = 10
patch_dim = 4
return VisionTransformer(
img_dim=img_dim,
patch_dim=patch_dim,
out_dim=out_dim,
num_channels=3,
embedding_dim=1024,
num_heads=16,
num_layers=24,
hidden_dim=4096,
dropout_rate=0.1,
attn_dropout_rate=0.0,
use_representation=False,
conv_patch_representation=False,
positional_encoding_type="learned",
)
def ViT_L32(dataset='imagenet'):
if dataset == 'imagenet':
img_dim = 224
out_dim = 1000
patch_dim = 32
elif 'cifar' in dataset:
img_dim = 32
out_dim = 10
patch_dim = 4
return VisionTransformer(
img_dim=img_dim,
patch_dim=patch_dim,
out_dim=out_dim,
num_channels=3,
embedding_dim=1024,
num_heads=16,
num_layers=24,
hidden_dim=4096,
dropout_rate=0.1,
attn_dropout_rate=0.0,
use_representation=False,
conv_patch_representation=False,
positional_encoding_type="learned",
)
def ViT_H14(dataset='imagenet'):
if dataset == 'imagenet':
img_dim = 224
out_dim = 1000
patch_dim = 14
elif 'cifar' in dataset:
img_dim = 32
out_dim = 10
patch_dim = 4
return VisionTransformer(
img_dim=img_dim,
patch_dim=patch_dim,
out_dim=out_dim,
num_channels=3,
embedding_dim=1280,
num_heads=16,
num_layers=32,
hidden_dim=5120,
dropout_rate=0.1,
attn_dropout_rate=0.0,
use_representation=False,
conv_patch_representation=False,
positional_encoding_type="learned",
)

View File

@@ -0,0 +1,44 @@
import torch
import torch.nn as nn
class FixedPositionalEncoding(nn.Module):
def __init__(self, embedding_dim, max_length=5000):
super(FixedPositionalEncoding, self).__init__()
pe = torch.zeros(max_length, embedding_dim)
position = torch.arange(0, max_length, dtype=torch.float).unsqueeze(1)
div_term = torch.exp(
torch.arange(0, embedding_dim, 2).float()
* (-torch.log(torch.tensor(10000.0)) / embedding_dim)
)
pe[:, 0::2] = torch.sin(position * div_term)
pe[:, 1::2] = torch.cos(position * div_term)
pe = pe.unsqueeze(0).transpose(0, 1)
self.register_buffer('pe', pe)
def forward(self, x):
x = x + self.pe[: x.size(0), :]
return x
class LearnedPositionalEncoding(nn.Module):
def __init__(self, max_position_embeddings, embedding_dim, seq_length):
super(LearnedPositionalEncoding, self).__init__()
self.pe = nn.Embedding(max_position_embeddings, embedding_dim)
self.seq_length = seq_length
self.register_buffer(
"position_ids",
torch.arange(max_position_embeddings).expand((1, -1)),
)
def forward(self, x, position_ids=None):
if position_ids is None:
position_ids = self.position_ids[:, : self.seq_length]
try:
position_embeddings = self.pe(position_ids)
return x + position_embeddings
except:
import pdb; pdb.set_trace()

View File

@@ -0,0 +1,82 @@
from torch import nn
from .Attention import SelfAttention
class Residual(nn.Module):
def __init__(self, fn):
super().__init__()
self.fn = fn
def forward(self, x):
return self.fn(x) + x
class PreNorm(nn.Module):
def __init__(self, dim, fn):
super().__init__()
self.norm = nn.LayerNorm(dim)
self.fn = fn
def forward(self, x):
return self.fn(self.norm(x))
class PreNormDrop(nn.Module):
def __init__(self, dim, dropout_rate, fn):
super().__init__()
self.norm = nn.LayerNorm(dim)
self.dropout = nn.Dropout(p=dropout_rate)
self.fn = fn
def forward(self, x):
return self.dropout(self.fn(self.norm(x)))
class FeedForward(nn.Module):
def __init__(self, dim, hidden_dim, dropout_rate):
super().__init__()
self.net = nn.Sequential(
nn.Linear(dim, hidden_dim),
nn.GELU(),
nn.Dropout(p=dropout_rate),
nn.Linear(hidden_dim, dim),
nn.Dropout(p=dropout_rate),
)
def forward(self, x):
return self.net(x)
class TransformerModel(nn.Module):
def __init__(
self,
dim,
depth,
heads,
mlp_dim,
dropout_rate=0.1,
attn_dropout_rate=0.1,
):
super().__init__()
layers = []
for _ in range(depth):
layers.extend(
[
Residual(
PreNormDrop(
dim,
dropout_rate,
SelfAttention(
dim, heads=heads, dropout_rate=attn_dropout_rate
),
)
),
Residual(
PreNorm(dim, FeedForward(dim, mlp_dim, dropout_rate))
),
]
)
self.net = nn.Sequential(*layers)
def forward(self, x):
return self.net(x)

View File

@@ -0,0 +1,350 @@
import torch
import torch.nn as nn
import torch.nn.functional as F
from configs.feature_sizes import FEATURE_SIZES
from model.model_builder import META_ARCHITECTURES as registry
from .attn import AttentionLayer, FullAttention, ProbAttention
from .decoder import Decoder, DecoderLayer
from .PositionalEncoding import (FixedPositionalEncoding,
LearnedPositionalEncoding)
from .Transformer import TransformerModel
# @registry.register('TRANSFORMER')
@registry.register('OADTR')
class OADTR(nn.Module):
def __init__(self, cfg, use_representation=True,
conv_patch_representation=False,
positional_encoding_type="learned"
):
super(OADTR, self).__init__()
self.img_dim = cfg["window_size"] # img_dim
self.out_dim = cfg["num_classes"]
self.patch_dim = cfg["patch_dim"]
self.query_num = cfg["query_num"]
self.embedding_dim = cfg["embedding_dim"]
self.num_heads = cfg["num_heads"]
self.num_layers = cfg["num_layers"]
self.hidden_dim = cfg["hidden_dim"]
self.dropout_rate = cfg["dropout"]
self.attn_dropout_rate = cfg["attn_dropout_rate"]
self.conv_patch_representation = conv_patch_representation
self.decoder_embedding_dim = cfg["decoder_embedding_dim"]
self.decoder_num_heads = cfg["decoder_num_heads"]
self.decoder_layers = cfg["decoder_layers"]
self.decoder_embedding_dim_out = cfg["decoder_embedding_dim_out"]
self.decoder_attn_dropout_rate = cfg["decoder_attn_dropout_rate"]
self.use_flow = not cfg['no_flow']
self.use_rgb = not cfg['no_rgb']
self.num_channels = 0
if self.use_rgb:
self.num_channels += FEATURE_SIZES[cfg['rgb_type']]
if self.use_flow:
self.num_channels += FEATURE_SIZES[cfg['flow_type']]
assert self.embedding_dim % self.num_heads == 0
assert self.img_dim % self.patch_dim == 0
self.num_patches = int(self.img_dim // self.patch_dim)
self.seq_length = self.num_patches + 1
# self.seq_length = self.num_patches + self.img_dim
self.flatten_dim = self.patch_dim * self.patch_dim * self.num_channels
self.cls_token = nn.Parameter(torch.zeros(1, 1, self.embedding_dim))
# self.cls_token = nn.Parameter(torch.zeros(1, self.img_dim, self.embedding_dim))
self.linear_encoding = nn.Linear(self.flatten_dim, self.embedding_dim)
if positional_encoding_type == "learned":
self.position_encoding = LearnedPositionalEncoding(
self.seq_length, self.embedding_dim, self.seq_length
)
elif positional_encoding_type == "fixed":
self.position_encoding = FixedPositionalEncoding(
self.embedding_dim,
)
print('position encoding :', positional_encoding_type)
self.pe_dropout = nn.Dropout(p=self.dropout_rate)
self.encoder = TransformerModel(
self.embedding_dim,
self.num_layers,
self.num_heads,
self.hidden_dim,
self.dropout_rate,
self.attn_dropout_rate,
)
self.pre_head_ln = nn.LayerNorm(self.embedding_dim)
d_model = self.decoder_embedding_dim
use_representation = False # False
if use_representation:
self.mlp_head = nn.Sequential(
nn.Linear(self.embedding_dim , self.hidden_dim//2),
# nn.Tanh(),
nn.ReLU(),
nn.Linear(self.hidden_dim//2, self.out_dim),
)
else:
self.mlp_head = nn.Linear(self.embedding_dim + d_model, self.out_dim)
if self.conv_patch_representation:
# self.conv_x = nn.Conv2d(
# self.num_channels,
# self.embedding_dim,
# kernel_size=(self.patch_dim, self.patch_dim),
# stride=(self.patch_dim, self.patch_dim),
# padding=self._get_padding(
# 'VALID', (self.patch_dim, self.patch_dim),
# ),
# )
self.conv_x = nn.Conv1d(
self.num_channels,
self.embedding_dim,
kernel_size=self.patch_dim,
stride=self.patch_dim,
padding=self._get_padding(
'VALID', (self.patch_dim),
),
)
else:
self.conv_x = None
self.to_cls_token = nn.Identity()
# Decoder
factor = 1 # 5
dropout = self.decoder_attn_dropout_rate
# d_model = self.decoder_embedding_dim
n_heads = self.decoder_num_heads
d_layers = self.decoder_layers
d_ff = self.decoder_embedding_dim_out # args.decoder_embedding_dim_out or 4*args.decoder_embedding_dim None
activation = 'gelu' # 'gelu'
self.decoder = Decoder(
[
DecoderLayer(
AttentionLayer(FullAttention(True, factor, attention_dropout=dropout), # True
d_model, n_heads), # ProbAttention FullAttention
AttentionLayer(FullAttention(False, factor, attention_dropout=dropout), # False
d_model, n_heads),
d_model,
d_ff,
dropout=dropout,
activation=activation,
)
for l in range(d_layers)
],
norm_layer=torch.nn.LayerNorm(d_model)
)
self.decoder_cls_token = nn.Parameter(torch.zeros(1, self.query_num, d_model))
if positional_encoding_type == "learned":
self.decoder_position_encoding = LearnedPositionalEncoding(
self.query_num, self.embedding_dim, self.query_num
)
elif positional_encoding_type == "fixed":
self.decoder_position_encoding = FixedPositionalEncoding(
self.embedding_dim,
)
print('position decoding :', positional_encoding_type)
self.classifier = nn.Linear(d_model, self.out_dim)
self.after_dropout = nn.Dropout(p=self.dropout_rate)
# self.merge_fc = nn.Linear(d_model, 1)
# self.merge_sigmoid = nn.Sigmoid()
def forward(self, sequence_input_rgb, sequence_input_flow):
if self.use_rgb and self.use_flow:
x = torch.cat((sequence_input_rgb, sequence_input_flow), 2)
elif self.use_rgb:
x = sequence_input_rgb
elif self.use_flow:
x = sequence_input_flow
x = self.linear_encoding(x) # B, 128, 1204
cls_tokens = self.cls_token.expand(x.shape[0], -1, -1) # B, 1, 1024
# x = torch.cat((cls_tokens, x), dim=1)
x = torch.cat((x, cls_tokens), dim=1) # B, seq+1, 1024
try:
x = self.position_encoding(x) # B, seq+1, 1024
except:
import pdb; pdb.set_trace()
x = self.pe_dropout(x) # not delete
# apply transformer
x = self.encoder(x)
x = self.pre_head_ln(x) # B, seq+1, 1024
# decoder
decoder_cls_token = self.decoder_cls_token.expand(x.shape[0], -1, -1) # [B, 8, 1024]
# decoder_cls_token = self.after_dropout(decoder_cls_token) # add
# decoder_cls_token = self.decoder_position_encoding(decoder_cls_token) # [128, 8, 1024]
dec = self.decoder(decoder_cls_token, x) # [128, 8, 1024]
dec = self.after_dropout(dec) # add
# merge_atte = self.merge_sigmoid(self.merge_fc(dec)) # [128, 8, 1]
# dec_for_token = (merge_atte*dec).sum(dim=1) # [128, 1024]
# dec_for_token = (merge_atte*dec).sum(dim=1)/(merge_atte.sum(dim=-2) + 0.0001)
# *** RuntimeError: mat1 and mat2 shapes cannot be multiplied (64x2048 and 1024x2)
dec_for_token = dec.mean(dim=1) # [64, 1024]
# dec_for_token = dec.max(dim=1)[0]
dec_cls_out = self.classifier(dec) # [B, 8, NUM_CLASS?]
# set_trace()
# x = self.to_cls_token(x[:, 0])
x = torch.cat((self.to_cls_token(x[:, -1]), dec_for_token), dim=1) # [B, 2048]
x = self.mlp_head(x) # [B, NUM_CLASSES]
# original OADTR
# return x, dec_cls_out
# x = F.log_softmax(x, dim=-1)
out_dict = {}
out_dict['logits'] = x.unsqueeze(1) # [B, 1, NUM_CLASSES] ex) [64, 1, 2]
out_dict['dec_logits'] = dec_cls_out # [B, 8, NUM_CLASSES] ex) [64, 8, 2]
# out_dict['logits'] = x #x.unsqueeze(1)
return out_dict
def _get_padding(self, padding_type, kernel_size):
assert padding_type in ['SAME', 'VALID']
if padding_type == 'SAME':
_list = [(k - 1) // 2 for k in kernel_size]
return tuple(_list)
return tuple(0 for _ in kernel_size)
@registry.register('Transformer')
class ViTEnc(nn.Module):
def __init__(self, cfg, use_representation=True,
conv_patch_representation=False,
positional_encoding_type="learned"
):
super(ViTEnc, self).__init__()
self.img_dim = cfg["window_size"]
self.out_dim = cfg["num_classes"]
self.embedding_dim = cfg["embedding_dim"]
self.patch_dim = cfg["patch_dim"]
self.num_heads = cfg["num_heads"]
self.num_layers = cfg["num_layers"]
self.hidden_dim = cfg["hidden_dim"]
self.dropout_rate = cfg["dropout"]
self.use_flow = not cfg['no_flow']
self.use_rgb = not cfg['no_rgb']
self.num_channels= 0
if self.use_rgb:
self.num_channels += FEATURE_SIZES[cfg['rgb_type']]
if self.use_flow:
self.num_channels += FEATURE_SIZES[cfg['flow_type']]
self.attn_dropout_rate = cfg["attn_dropout_rate"]
assert self.embedding_dim % self.num_heads == 0
assert self.img_dim % self.patch_dim == 0
self.conv_patch_representation = conv_patch_representation
self.num_patches = int(self.img_dim // self.patch_dim)
self.seq_length = self.num_patches + 1
# self.seq_length = self.num_patches + self.img_dim
self.flatten_dim = self.patch_dim * self.patch_dim * self.num_channels
self.cls_token = nn.Parameter(torch.zeros(1, 1, self.embedding_dim))
# self.cls_token = nn.Parameter(torch.zeros(1, self.img_dim, self.embedding_dim))
self.linear_encoding = nn.Linear(self.flatten_dim, self.embedding_dim)
if positional_encoding_type == "learned":
self.position_encoding = LearnedPositionalEncoding(
self.seq_length, self.embedding_dim, self.seq_length
)
elif positional_encoding_type == "fixed":
self.position_encoding = FixedPositionalEncoding(
self.embedding_dim,
)
print('position encoding :', positional_encoding_type)
self.pe_dropout = nn.Dropout(p=self.dropout_rate)
self.encoder = TransformerModel(
self.embedding_dim,
self.num_layers,
self.num_heads,
self.hidden_dim,
self.dropout_rate,
self.attn_dropout_rate,
)
self.pre_head_ln = nn.LayerNorm(self.embedding_dim)
use_representation = False # False
if use_representation:
self.mlp_head = nn.Sequential(
nn.Linear(self.embedding_dim , self.hidden_dim//2),
# nn.Tanh(),
nn.ReLU(),
nn.Linear(self.hidden_dim//2, self.out_dim),
)
else:
self.mlp_head = nn.Linear(self.embedding_dim, self.out_dim)
if self.conv_patch_representation:
# self.conv_x = nn.Conv2d(
# self.num_channels,
# self.embedding_dim,
# kernel_size=(self.patch_dim, self.patch_dim),
# stride=(self.patch_dim, self.patch_dim),
# padding=self._get_padding(
# 'VALID', (self.patch_dim, self.patch_dim),
# ),
# )
self.conv_x = nn.Conv1d(
self.num_channels,
self.embedding_dim,
kernel_size=self.patch_dim,
stride=self.patch_dim,
padding=self._get_padding(
'VALID', (self.patch_dim),
),
)
else:
self.conv_x = None
self.to_cls_token = nn.Identity()
def forward(self, sequence_input_rgb, sequence_input_flow):
if self.use_rgb and self.use_flow:
x = torch.cat((sequence_input_rgb, sequence_input_flow), 2)
elif self.use_rgb:
x = sequence_input_rgb
elif self.use_flow:
x = sequence_input_flow
x = self.linear_encoding(x)
cls_tokens = self.cls_token.expand(x.shape[0], -1, -1) # B, 1, 1024
# x = torch.cat((cls_tokens, x), dim=1)
x = torch.cat((x, cls_tokens), dim=1) # B, seq+1, 1024
x = self.position_encoding(x) # B, seq+1, 1024
x = self.pe_dropout(x) # not delete
# apply transformer
x = self.encoder(x)
x = self.pre_head_ln(x) # B, seq+1, 1024
x = self.to_cls_token(x[:, 0]) # B, 1024
# x = self.to_cls_token(x[:,0:self.img_dim]) # B, 1024
x = self.mlp_head(x)
# x = F.log_softmax(x, dim=-1)
out_dict = {}
out_dict['logits'] = x.unsqueeze(1)
# out_dict['logits'] = x #x.unsqueeze(1)
return out_dict
def _get_padding(self, padding_type, kernel_size):
assert padding_type in ['SAME', 'VALID']
if padding_type == 'SAME':
_list = [(k - 1) // 2 for k in kernel_size]
return tuple(_list)
return tuple(0 for _ in kernel_size)

Some files were not shown because too many files have changed in this diff Show More