1.0.0 official release (#18)

* fix bug in fix_BN

* gaitgl OUMVLP support.

* update ./doc/3.advance_usage.md Cross-Dataset Evalution & Data Agumentation

* update config

* update docs.3

* update docs.3

* add loss doc and gather input decorator

* refine the create model doc

* support rearrange directory of unzipped OUMVLP

* fix some bugs in loss_aggregator.py

* refine docs and little fix

* add oumvlp pretreatment description

* pretreatment dataset fix oumvlp description

* add gaitgl oumvlp result

* assert gaitgl input size

* add pipeline

* update the readme.

* update pipeline and readme

* Corrigendum.

* add logo and remove path

* update new logo

* Update README.md

* modify logo size

Co-authored-by: 12131100 <12131100@mail.sustech.edu.cn>
Co-authored-by: noahshen98 <77523610+noahshen98@users.noreply.github.com>
Co-authored-by: Noah <595311942@qq.com>
This commit is contained in:
Junhao Liang
2021-12-08 20:05:28 +08:00
committed by GitHub
parent 6e71c7ac34
commit bb6cd5149a
39 changed files with 11401 additions and 230 deletions
+60 -41
View File
@@ -1,40 +1,49 @@
**Note:**
This code is only used for **academic purposes**, people cannot use this code for anything that might be considered commercial use.
<img src="./assets/logo2.png" width = "320" height = "110" alt="logo" />
# OpenGait
<div align="center"><img src="./assets/nm.gif" width = "100" height = "100" alt="nm" /><img src="./assets/bg.gif" width = "100" height = "100" alt="bg" /><img src="./assets/cl.gif" width = "100" height = "100" alt="cl" /></div>
OpenGait is a flexible and extensible gait recognition project provided by the [Shiqi Yu Group](https://faculty.sustech.edu.cn/yusq/) and supported in part by [WATRIX.AI](http://www.watrix.ai). Just the pre-beta version is released now, and more documentations as well as the reproduced methods will be offered as soon as possible.
------------------------------------------
OpenGait is a flexible and extensible gait recognition project provided by the [Shiqi Yu Group](https://faculty.sustech.edu.cn/yusq/) and supported in part by [WATRIX.AI](http://www.watrix.ai).
**Highlighted features:**
- **Multiple Models Support**: We reproduced several SOTA methods, and reached the same or even better performance.
- **DDP Support**: The officially recommended [`Distributed Data Parallel (DDP)`](https://pytorch.org/tutorials/intermediate/ddp_tutorial.html) mode is used during the training and testing phases.
- **Multiple Models Support**: We reproduced several SOTA methods, and reached the same or even the better performance.
- **DDP Support**: The officially recommended [`Distributed Data Parallel (DDP)`](https://pytorch.org/tutorials/intermediate/ddp_tutorial.html) mode is used during both the training and testing phases.
- **AMP Support**: The [`Auto Mixed Precision (AMP)`](https://pytorch.org/tutorials/recipes/recipes/amp_recipe.html?highlight=amp) option is available.
- **Nice log**: We use [`tensorboard`](https://pytorch.org/docs/stable/tensorboard.html) and `logging` to log everything, which looks pretty.
# Model Zoo
## Model Zoo
### CASIA-B
| Model | NM | BG | CL | Configuration | Input Size | Inference Time | Model Size |
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------: | :--------: | :--------: | :------------------------------------------------------------------------------------------- | :--------: | :------------: | :------------: |
| Baseline | 96.3 | 92.2 | 77.6 | [baseline.yaml](config/baseline.yaml) | 64x44 | 12s | 3.78M |
| [GaitSet(AAAI2019)](https://arxiv.org/pdf/1811.06186.pdf) | 95.8(95.0) | 90.0(87.2) | 75.4(70.4) | [gaitset.yaml](config/gaitset.yaml) | 64x44 | 11s | 2.59M |
| [GaitPart(CVPR2020)](http://home.ustc.edu.cn/~saihui/papers/cvpr2020_gaitpart.pdf) | 96.1(96.2) | 90.7(91.5) | 78.7(78.7) | [gaitpart.yaml](config/gaitpart.yaml) | 64x44 | 22s | 1.20M |
| [GLN*(ECCV2020)](http://home.ustc.edu.cn/~saihui/papers/eccv2020_gln.pdf) | 96.4(95.6) | 93.1(92.0) | 81.0(77.2) | [gln_phase1.yaml](config/gln/gln_phase1.yaml), [gln_phase2.yaml](config/gln/gln_phase2.yaml) | 128x88 | 14s | 8.54M / 14.70M |
| [GaitGL(ICCV2021)](https://openaccess.thecvf.com/content/ICCV2021/papers/Lin_Gait_Recognition_via_Effective_Global-Local_Feature_Representation_and_Local_Temporal_ICCV_2021_paper.pdf) | 97.4(97.4) | 94.5(94.5) | 83.8(83.6) | [gaitgl.yaml](config/gaitgl.yaml) | 64x44 | 31s | 3.10M |
| [GaitSet(AAAI2019)](https://arxiv.org/pdf/1811.06186.pdf) | 95.8(95.0) | 90.0(87.2) | 75.4(70.4) | [gaitset.yaml](config/gaitset.yaml) | 64x44 | 13s | 2.59M |
| [GaitPart(CVPR2020)](http://home.ustc.edu.cn/~saihui/papers/cvpr2020_gaitpart.pdf) | 96.1(96.2) | 90.7(91.5) | 78.7(78.7) | [gaitpart.yaml](config/gaitpart.yaml) | 64x44 | 56s | 1.20M |
| [GLN*(ECCV2020)](http://home.ustc.edu.cn/~saihui/papers/eccv2020_gln.pdf) | 96.4(95.6) | 93.1(92.0) | 81.0(77.2) | [gln_phase1.yaml](config/gln/gln_phase1.yaml), [gln_phase2.yaml](config/gln/gln_phase2.yaml) | 128x88 | 47s/46s | 8.54M / 14.70M |
| [GaitGL(ICCV2021)](https://openaccess.thecvf.com/content/ICCV2021/papers/Lin_Gait_Recognition_via_Effective_Global-Local_Feature_Representation_and_Local_Temporal_ICCV_2021_paper.pdf) | 97.4(97.4) | 94.5(94.5) | 83.8(83.6) | [gaitgl.yaml](config/gaitgl.yaml) | 64x44 | 38s | 3.10M |
### OUMVLP
| Model | Rank@1 | Configuration | Input Size | Inference Time | Model Size |
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------: | :------------------------------------------: | :--------: | :------------- | :--------: |
| Baseline | 86.7 | [baseline.yaml](config/OUMVLP/baseline.yaml) | 64x44 | 1m13s | 44.11M |
| [GaitSet(AAAI2019)](https://arxiv.org/pdf/1811.06186.pdf) | 87.2(87.1) | [gaitset.yaml](config/OUMVLP/gaitset.yaml) | 64x44 | 1m26s | 6.31M |
| [GaitPart(CVPR2020)](http://home.ustc.edu.cn/~saihui/papers/cvpr2020_gaitpart.pdf) | 88.6(88.7) | [gaitpart.yaml](config/OUMVLP/gaitpart.yaml) | 64x44 | 8m04s | 3.78M |
| [GaitGL(ICCV2021)](https://openaccess.thecvf.com/content/ICCV2021/papers/Lin_Gait_Recognition_via_Effective_Global-Local_Feature_Representation_and_Local_Temporal_ICCV_2021_paper.pdf) | 89.9(89.7) | [gaitgl.yaml](config/OUMVLP/gaitgl.yaml) | 64x44 | 5m23s | 95.62M |
The results in the parentheses are mentioned in the papers
**Note**:
- All the models were tested on [CASIA-B](http://www.cbsr.ia.ac.cn/english/Gait%20Databases.asp) (Rank@1, excluding identical-view cases).
- All results are Rank@1, excluding identical-view cases.
- The shown result of GLN is implemented without compact block.
- Only 2 RTX6000 are used during the inference phase.
- The results on [OUMVLP](http://www.am.sanken.osaka-u.ac.jp/BiometricDB/GaitMVLP.html) will be released soon.
It's inference process just cost about 90 secs(Baseline & 8 RTX6000).
- Only two RTX3090 are used for infering CASIA-B, and eight are used for infering OUMVLP.
# Get Started
## Installation
## Get Started
### Installation
1. clone this repo.
```
git clone https://github.com/ShiqiYu/OpenGait.git
@@ -57,56 +66,66 @@ It's inference process just cost about 90 secs(Baseline & 8 RTX6000).
pip install tqdm pyyaml tensorboard opencv-python
pip install torch==1.6.0 torchvision==0.7.0
```
## Prepare dataset
See [prepare dataset](doc/prepare_dataset.md).
### Prepare dataset
See [prepare dataset](docs/0.prepare_dataset.md).
## Get trained model
### Get trained model
- Option 1:
```
python misc/download_pretrained_model.py
```
- Option 2: Go to the [release page](https://github.com/ShiqiYu/OpenGait/releases/), then download the model file and uncompress it to `output`.
- Option 2: Go to the [release page](https://github.com/ShiqiYu/OpenGait/releases/), then download the model file and uncompress it to [output](output).
## Train
### Train
Train a model by
```
CUDA_VISIBLE_DEVICES=0,1 python -m torch.distributed.launch --nproc_per_node=2 lib/main.py --cfgs ./config/baseline.yaml --phase train
```
- `python -m torch.distributed.launch` Our implementation uses DistributedDataParallel.
- `--nproc_per_node` The number of gpu to use, it must equal the length of `CUDA_VISIBLE_DEVICES`.
- `--cfgs` The path of config file.
- `python -m torch.distributed.launch` [DDP](https://pytorch.org/tutorials/intermediate/ddp_tutorial.html) launch instruction.
- `--nproc_per_node` The number of gpus to use, and it must equal the length of `CUDA_VISIBLE_DEVICES`.
- `--cfgs` The path to config file.
- `--phase` Specified as `train`.
- `--iter` You can specify a number of iterations or use `restore_hint` in the configuration file and resume training from there.
- `--log_to_file` If specified, log will be written on disk simultaneously.
<!-- - `--iter` You can specify a number of iterations or use `restore_hint` in the config file and resume training from there. -->
- `--log_to_file` If specified, the terminal log will be written on disk simultaneously.
You can run commands in [train.sh](train.sh) for training different models.
## Test
Use trained model to evaluate by
### Test
Evaluate the trained model by
```
CUDA_VISIBLE_DEVICES=0,1 python -m torch.distributed.launch --nproc_per_node=2 lib/main.py --cfgs ./config/baseline.yaml --phase test
```
- `--phase` Specified as `test`.
- `--iter` You can specify a number of iterations or or use `restore_hint` in the configuration file and restore model from there.
- `--iter` Specify a iteration checkpoint.
**Tip**: Other arguments are the same as train phase.
You can run commands in [test.sh](test.sh) for testing different models.
## Customize
1. First, you need to read the [config documentation](doc/detailed_config.md) to figure out the usage of every item.
2. If you want create your own model, see [here](doc/how_to_create_your_model.md).
1. Read the [detailed config](docs/1.detailed_config.md) to figure out the usage of needed setting items;
2. See [how to create your model](docs/2.how_to_create_your_model.md);
3. There are some advanced usages, refer to [advanced usages](docs/3.advanced_usages.md), please.
# Warning
## Warning
- Some models may not be compatible with `AMP`, you can disable it by setting `enable_float16` **False**.
- In `DDP` mode, zombie processes may be generated when the program terminates abnormally. You can use this command `kill $(ps aux | grep main.py | grep -v grep | awk '{print $2}')` to clear them.
- In `DDP` mode, zombie processes may be generated when the program terminates abnormally. You can use this command [sh misc/clean_process.sh](./misc/clean_process.sh) to clear them.
- We implemented the functionality about testing while training, but it slightly affected the results. None of our published models use this functionality. You can disable it by setting `with_test` **False**.
- Recommended Pytorch version: 1.6-1.8
# Authors:
## Authors:
**Open Gait Team (OGT)**
- [Chao Fan (樊超)](https://faculty.sustech.edu.cn/?p=128578&tagid=yusq&cat=2&iscss=1&snapid=1&orderby=date)
- [Chuanfu Shen (沈川福)](https://faculty.sustech.edu.cn/?p=95396&tagid=yusq&cat=2&iscss=1&snapid=1&orderby=date)
- [Junhao Liang (梁峻豪)](https://faculty.sustech.edu.cn/?p=95401&tagid=yusq&cat=2&iscss=1&snapid=1&orderby=date)
- [Chao Fan (樊超)](https://faculty.sustech.edu.cn/?p=128578&tagid=yusq&cat=2&iscss=1&snapid=1&orderby=date), 12131100@mail.sustech.edu.cn
- [Chuanfu Shen (沈川福)](https://faculty.sustech.edu.cn/?p=95396&tagid=yusq&cat=2&iscss=1&snapid=1&orderby=date), 11950016@mail.sustech.edu.cn
- [Junhao Liang (梁峻豪)](https://faculty.sustech.edu.cn/?p=95401&tagid=yusq&cat=2&iscss=1&snapid=1&orderby=date), 12132342@mail.sustech.edu.cn
# Acknowledgement
## Acknowledgement
- GLN: [Saihui Hou (侯赛辉)](http://home.ustc.edu.cn/~saihui/index_english.html)
- GaitGL: Beibei Lin (林贝贝)
- GaitGL: [Beibei Lin (林贝贝)](https://scholar.google.com/citations?user=KyvHam4AAAAJ&hl=en&oi=ao)
<!-- ## Citation
```
``` -->
**Note:**
This code is only used for **academic purposes**, people cannot use this code for anything that might be considered commercial use.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

+8 -8
View File
@@ -1,6 +1,6 @@
data_cfg:
dataset_name: CASIA-B
dataset_root: your_path
dataset_root: your_path
dataset_partition: ./misc/partitions/CASIA-B_include_005.json
num_workers: 1
remove_no_gallery: false # Remove probe if no gallery for it
@@ -18,9 +18,9 @@ evaluator_cfg:
sample_type: all_ordered # all indicates whole sequence used to test, while ordered means input sequence by its natural order; Other options: fixed_unordered
frames_all_limit: 720 # limit the number of sampled frames to prevent out of memory
metric: euc # cos
# transform:
# - type: BaseSilCuttingTransform
# img_w: 128
transform:
- type: BaseSilCuttingTransform
img_w: 64
loss_cfg:
- loss_term_weights: 1.0
@@ -79,7 +79,7 @@ scheduler_cfg:
scheduler: MultiStepLR
trainer_cfg:
enable_float16: true # half_percesion float for memory reduction and speedup
fix_BN: false
fix_BN: true
log_iter: 100
restore_ckpt_strict: true
restore_hint: 0
@@ -97,6 +97,6 @@ trainer_cfg:
frames_num_min: 25 # min frames number for unfixed traing
sample_type: fixed_unordered # fixed control input frames number, unordered for controlling order of input tensor; Other options: unfixed_ordered or all_ordered
type: TripletSampler
# transform:
# - type: BaseSilCuttingTransform
# img_w: 128
transform:
- type: BaseSilCuttingTransform
img_w: 64
+102
View File
@@ -0,0 +1,102 @@
data_cfg:
dataset_name: OUMVLP
dataset_root: your_path
dataset_partition: ./misc/partitions/OUMVLP.json
num_workers: 1
remove_no_gallery: false # Remove probe if no gallery for it
test_dataset_name: OUMVLP
evaluator_cfg:
enable_float16: true
restore_ckpt_strict: true
restore_hint: 150000
save_name: Baseline
eval_func: identification
sampler:
batch_shuffle: false
batch_size: 4
sample_type: all_ordered # all indicates whole sequence used to test, while ordered means input sequence by its natural order; Other options: fixed_unordered
frames_all_limit: 720 # limit the number of sampled frames to prevent out of memory
metric: euc # cos
# transform:
# - type: BaseSilCuttingTransform
# img_w: 128
loss_cfg:
- loss_term_weights: 1.0
margin: 0.2
type: TripletLoss
log_prefix: triplet
- loss_term_weights: 0.1
scale: 16
type: CrossEntropyLoss
log_prefix: softmax
log_accuracy: true
model_cfg:
model: Baseline
backbone_cfg:
in_channels: 1
layers_cfg: # Layers configuration for automatically model construction
- BC-32
- BC-32
- M
- BC-64
- BC-64
- M
- BC-128
- BC-128
- BC-256
- BC-256
type: Plain
SeparateFCs:
in_channels: 256
out_channels: 256
parts_num: 31
SeparateBNNecks:
class_num: 5153
in_channels: 256
parts_num: 31
bin_num:
- 16
- 8
- 4
- 2
- 1
optimizer_cfg:
lr: 0.1
momentum: 0.9
solver: SGD
weight_decay: 0.0005
scheduler_cfg:
gamma: 0.1
milestones: # Learning Rate Reduction at each milestones
- 50000
- 100000
scheduler: MultiStepLR
trainer_cfg:
enable_float16: true # half_percesion float for memory reduction and speedup
fix_BN: false
log_iter: 100
with_test: true
restore_ckpt_strict: true
restore_hint: 0
save_iter: 10000
save_name: Baseline
sync_BN: true
total_iter: 150000
sampler:
batch_shuffle: true
batch_size:
- 32 # TripletSampler, batch_size[0] indicates Number of Identity
- 16 # batch_size[1] indicates Samples sequqnce for each Identity
frames_num_fixed: 30 # fixed frames number for training
frames_num_max: 50 # max frames number for unfixed training
frames_num_min: 25 # min frames number for unfixed traing
sample_type: fixed_unordered # fixed control input frames number, unordered for controlling order of input tensor; Other options: unfixed_ordered or all_ordered
type: TripletSampler
# transform:
# - type: BaseSilCuttingTransform
# img_w: 128
-1
View File
@@ -49,7 +49,6 @@ scheduler_cfg:
trainer_cfg:
enable_distributed: true
enable_float16: false
fix_BN: false
log_iter: 100
restore_ckpt_strict: true
restore_hint: 0
+69
View File
@@ -0,0 +1,69 @@
# Note : *** the batch_size should be equal to the gpus number at the test phase!!! ***
data_cfg:
dataset_name: OUMVLP
dataset_root: your_path
dataset_partition: ./misc/partitions/OUMVLP.json
num_workers: 1
remove_no_gallery: false
test_dataset_name: OUMVLP
evaluator_cfg:
enable_distributed: true
enable_float16: false
restore_ckpt_strict: true
restore_hint: 210000
save_name: GaitGL
sampler:
batch_size: 2
sample_type: all_ordered
type: InferenceSampler
loss_cfg:
- loss_term_weights: 1.0
margin: 0.2
type: TripletLoss
log_prefix: triplet
- loss_term_weights: 1.0
scale: 1
type: CrossEntropyLoss
log_accuracy: true
label_smooth: true
log_prefix: softmax
model_cfg:
model: GaitGL
channels: [32, 64, 128, 256]
class_num: 5153
optimizer_cfg:
lr: 1.0e-4
solver: Adam
weight_decay: 0
scheduler_cfg:
gamma: 0.1
milestones:
- 150000
- 200000
scheduler: MultiStepLR
trainer_cfg:
enable_distributed: true
enable_float16: true
with_test: false
log_iter: 100
restore_ckpt_strict: true
restore_hint: 0
save_iter: 10000
save_name: GaitGL
sync_BN: true
total_iter: 210000
sampler:
batch_shuffle: true
batch_size:
- 32
- 8
frames_num_fixed: 30
frames_skip_num: 0
sample_type: fixed_ordered
type: TripletSampler
-1
View File
@@ -58,7 +58,6 @@ scheduler_cfg:
trainer_cfg:
enable_float16: true
fix_BN: false
log_iter: 100
restore_ckpt_strict: true
restore_hint: 0
+82
View File
@@ -0,0 +1,82 @@
data_cfg:
dataset_name: OUMVLP
dataset_root: your_path
dataset_partition: ./misc/partitions/OUMVLP.json
num_workers: 4
remove_no_gallery: false
test_dataset_name: OUMVLP
evaluator_cfg:
enable_float16: false
restore_ckpt_strict: true
restore_hint: 250000
save_name: GaitPart
sampler:
batch_size: 4
sample_type: all_ordered
type: InferenceSampler
metric: euc # cos
loss_cfg:
loss_term_weights: 1.0
margin: 0.2
type: TripletLoss
log_prefix: triplet
model_cfg:
model: GaitPart
backbone_cfg:
in_channels: 1
layers_cfg:
- BC-32
- BC-32
- M
- BC-64
- BC-64
- M
- FC-128-3
- FC-128-3
- FC-256-3
- FC-256-3
type: Plain
SeparateFCs:
in_channels: 256
out_channels: 256
parts_num: 16
bin_num:
- 16
optimizer_cfg:
lr: 0.0001
momentum: 0.9
solver: Adam
weight_decay: 0.0
scheduler_cfg:
gamma: 0.1
milestones:
- 150000
scheduler: MultiStepLR
trainer_cfg:
enable_float16: true
fix_BN: false
log_iter: 100
with_test: true
restore_ckpt_strict: true
restore_hint: 0
save_iter: 10000
save_name: GaitPart
sync_BN: false
total_iter: 250000
sampler:
batch_shuffle: false
batch_size:
- 32
- 16
frames_num_fixed: 30
frames_num_max: 50
frames_num_min: 25
frames_skip_num: 10
sample_type: fixed_ordered
type: TripletSampler
-1
View File
@@ -56,7 +56,6 @@ scheduler_cfg:
trainer_cfg:
enable_float16: true
fix_BN: false
log_iter: 100
restore_ckpt_strict: true
restore_hint: 0
+76
View File
@@ -0,0 +1,76 @@
data_cfg:
dataset_name: OUMVLP
dataset_root: your_path
dataset_partition: ./misc/partitions/OUMVLP.json
num_workers: 4
remove_no_gallery: false
test_dataset_name: OUMVLP
evaluator_cfg:
enable_float16: true
restore_ckpt_strict: true
restore_hint: 250000
save_name: GaitSet
sampler:
batch_size: 4
sample_type: all_ordered
type: InferenceSampler
metric: euc # cos
loss_cfg:
loss_term_weights: 1.0
margin: 0.2
type: TripletLoss
log_prefix: triplet
model_cfg:
model: GaitSet
in_channels:
- 1
- 64
- 128
- 256
SeparateFCs:
in_channels: 256
out_channels: 256
parts_num: 62
bin_num:
- 16
- 8
- 4
- 2
- 1
optimizer_cfg:
lr: 0.0001
momentum: 0.9
solver: Adam
weight_decay: 0
scheduler_cfg:
gamma: 0.1
milestones:
- 150000
scheduler: MultiStepLR
trainer_cfg:
enable_float16: true
fix_BN: false
with_test: true
log_iter: 100
restore_ckpt_strict: true
restore_hint: 0
save_iter: 10000
save_name: GaitSet
sync_BN: false
total_iter: 250000
sampler:
batch_shuffle: false
batch_size:
- 32
- 16
frames_num_fixed: 30
frames_num_max: 50
frames_num_min: 25
sample_type: fixed_unordered
type: TripletSampler
+1 -1
View File
@@ -76,7 +76,7 @@ scheduler_cfg:
trainer_cfg:
enable_distributed: true
enable_float16: true
fix_BN: false
fix_layers: false
with_test: false
log_iter: 100
optimizer_reset: false
+1 -1
View File
@@ -71,7 +71,7 @@ scheduler_cfg:
trainer_cfg:
enable_distributed: true
enable_float16: true
fix_BN: false
fix_layers: false
log_iter: 100
optimizer_reset: true
scheduler_reset: true
-4
View File
@@ -1,4 +0,0 @@
# How to Create Your Own Model
This section of documentation will be **refined in the future**. For now, you can refer these files: [default config](../config/default.yaml), [baseline config](../config/baseline.yaml), [loss aggregator](../lib/modeling/loss_aggregator.py), [base_model](../lib/modeling/base_model.py), and [baseline model](../lib/modeling/models/baseline.py).
Then, you can write your own model in `lib\modeling\models`, and use it in configuration file.
-36
View File
@@ -1,36 +0,0 @@
# Prepare dataset
Suppose you have downloaded the original dataset, we need to preprocess the data and save it as pickle file. Remember to set your path to the root of processed dataset in [config/*.yaml](config/).
## Preprocess
**CASIA-B**
Download URL: http://www.cbsr.ia.ac.cn/GaitDatasetB-silh.zip
- Original
```
CASIA-B
001 (subject)
bg-01 (type)
000 (view)
001-bg-01-000-001.png (frame)
001-bg-01-000-002.png (frame)
......
......
......
......
```
- Run `python misc/pretreatment.py --input_path CASIA-B --output_path CASIA-B-pkl`
- Processed
```
CASIA-B-pkl
001 (subject)
bg-01 (type)
000 (view)
000.pkl (contains all frames)
......
......
......
```
## Split dataset
You can use the partition file in [misc/partitions](misc/partitions/) directly, or you can create yours. Remember to set your path to the partition file in [config/*.yaml](config/).
+101
View File
@@ -0,0 +1,101 @@
# Prepare dataset
Suppose you have downloaded the original dataset, we need to preprocess the data and save it as pickle file. Remember to set your path to the root of processed dataset in [config/*.yaml](config/).
## Preprocess
**CASIA-B**
Download URL: http://www.cbsr.ia.ac.cn/GaitDatasetB-silh.zip
- Original
```
CASIA-B
001 (subject)
bg-01 (type)
000 (view)
001-bg-01-000-001.png (frame)
001-bg-01-000-002.png (frame)
......
......
......
......
```
- Run `python misc/pretreatment.py --input_path CASIA-B --output_path CASIA-B-pkl`
- Processed
```
CASIA-B-pkl
001 (subject)
bg-01 (type)
000 (view)
000.pkl (contains all frames)
......
......
......
```
**OUMVLP**
Step1: Download URL: http://www.am.sanken.osaka-u.ac.jp/BiometricDB/GaitMVLP.html
Step2: Unzip the dataset, you will get a structure directory like:
- Original
```
OUMVLP-raw
Silhouette_000-00 (view-sequence)
00001 (subject)
0001.png (frame)
0002.png (frame)
......
00002
0001.png (frame)
0002.png (frame)
......
......
Silhouette_000-01
00001
0001.png (frame)
0002.png (frame)
......
00002
0001.png (frame)
0002.png (frame)
......
......
Silhouette_015-00
......
Silhouette_015-01
......
......
```
Step3 : To rearrange directory of OUMVLP dataset, turning to id-type-view structure, Run
```
python misc/rearrange_OUMVLP.py --input_path OUMVLP-raw --output_path OUMVLP-rearrange
```
Step4: Transforming images to pickle file, run
```
python misc/pretreatment.py --input_path OUMVLP-rearrange --output_path OUMVLP-pkl
```
- Processed
```
OUMVLP-pkl
00001 (subject)
00 (sequence)
000 (view)
000.pkl (contains all frames)
015 (view)
015.pkl (contains all frames)
...
01 (sequence)
000 (view)
000.pkl (contains all frames)
015 (view)
015.pkl (contains all frames)
......
00002 (subject)
......
......
```
## Split dataset
You can use the partition file in [misc/partitions](misc/partitions/) directly, or you can create yours. Remember to set your path to the partition file in [config/*.yaml](config/).
@@ -4,7 +4,7 @@
* Data configuration
>
> * Args
> * dataset_name: Dataset name. Only support `CASIA-B`.
> * dataset_name: Only support `CASIA-B` and `OUMVLP` now.
> * dataset_root: The path of storing your dataset.
> * num_workers: The number of workers to collect data.
> * dataset_partition: The path of storing your dataset partition file. It splits the dataset to two parts, including train set and test set.
@@ -15,7 +15,7 @@
### loss_cfg
* Loss function
> * Args
> * type: Loss function type, support `TripletLoss` and `CrossEntropyLoss`
> * type: Loss function type, support `TripletLoss` and `CrossEntropyLoss`.
> * loss_term_weights: loss weight.
> * log_prefix: the prefix of loss log.
@@ -23,36 +23,36 @@
### optimizer_cfg
* Optimizer
> * Args
> * solver: Optimizer type, example: `SGD`, `Adam`
> * **others**: Please refer to `torch.optim`
> * solver: Optimizer type, example: `SGD`, `Adam`.
> * **others**: Please refer to `torch.optim`.
### scheduler_cfg
* Learning rate scheduler
> * Args
> * scheduler : Learning rate scheduler, example: `MultiStepLR`
> * **others** : Please refer to `torch.optim.lr_scheduler`
> * scheduler : Learning rate scheduler, example: `MultiStepLR`.
> * **others** : Please refer to `torch.optim.lr_scheduler`.
----
### model_cfg
* Model to be trained
> * Args
> * model : Model type, please refer to [Model Library](../lib/modeling/models) for the supported values
> * **others** : Please refer to [Training Configuration File of Corresponding Model](../config)
> * model : Model type, please refer to [Model Library](../lib/modeling/models) for the supported values.
> * **others** : Please refer to the [Training Configuration File of Corresponding Model](../config).
----
### evaluator_cfg
* Evaluator configuration
> * Args
> * enable_float16: If `True`, enable auto mixed precision.
> * restore_ckpt_strict: If `True`, check whether the checkpoint is the same as the model.
> * restore_hint: `int` value indicates the iteration number of restored checkpoint; `str` value indicates the path of restored checkpoint.
> * enable_float16: If `True`, enable the auto mixed precision mode.
> * restore_ckpt_strict: If `True`, check whether the checkpoint is the same as the defined model.
> * restore_hint: `int` value indicates the iteration number of restored checkpoint; `str` value indicates the path to restored checkpoint.
> * save_name: The name of the experiment.
> * eval_func: The function name of evaluation. For `CASIA-B`, choose `identification`.
> * sampler:
> - type: The name of sampler. Choose `InferenceSampler`
> - type: The name of sampler. Choose `InferenceSampler`.
> - sample_type: In general, we use `all_ordered` to input all frames by its natural order, which makes sure the tests are consistent.
> - batch_size: In general, it should equal to the number of utilized GPU.
> - batch_size: `int` values.
> - **others**: Please refer to [data.sampler](../lib/data/sampler.py) and [data.collate_fn](../lib/data/collate_fn.py)
> * transform: support `BaseSilCuttingTransform`, `BaseSilTransform`. The difference between them is `BaseSilCuttingTransform` cut the pixels on both sides horizontally.
> * transform: Support `BaseSilCuttingTransform`, `BaseSilTransform`. The difference between them is `BaseSilCuttingTransform` cut out the black pixels on both sides horizontally.
> * metric: `euc` or `cos`, generally, `euc` performs better.
----
@@ -60,25 +60,25 @@
* Trainer configuration
> * Args
> * fix_BN: If `True`, we fix the weight of all `BatchNorm` layers.
> * log_iter: Every `log_iter` iterations, log the information.
> * save_iter: Every `save_iter` iterations, save the model.
> * with_test: If `True`, we test the model every `save_iter` iterations. A bit of performance impact.(*To Be Fixed*)
> * log_iter: Log the information per `log_iter` iterations.
> * save_iter: Save the checkpoint per `save_iter` iterations.
> * with_test: If `True`, we test the model every `save_iter` iterations. A bit of performance impact.(*Disable in Default*)
> * optimizer_reset: If `True` and `restore_hint!=0`, reset the optimizer while restoring the model.
> * scheduler_reset: If `True` and `restore_hint!=0`, reset the scheduler while restoring the model.
> * sync_BN: If `True`, applies Batch Normalization as described in the paper [Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift](https://arxiv.org/abs/1502.03167).
> * total_iter: The total number of training iterations.
> * sync_BN: If `True`, applies Batch Normalization synchronously.
> * total_iter: The total training iterations, `int` values.
> * sampler:
> - type: The name of sampler. Choose `TripletSampler`
> - type: The name of sampler. Choose `TripletSampler`.
> - sample_type: `[all, fixed, unfixed]` indicates the number of frames used to test, while `[unordered, ordered]` means whether input sequence by its natural order. Example: `fixed_unordered` means selecting fixed number of frames randomly.
> - batch_size: *[P,K]*\
> **example**:
> - batch_size: *[P,K]* where `P` denotes the subjects in training batch while the `K` represents the sequences every subject owns. **Example**:
> - 8
> - 16
> - **others**: Please refer to [data.sampler](../lib/data/sampler.py) and [data.collate_fn](../lib/data/collate_fn.py)
> * **others**: Please refer to `evaluator_cfg`
> - **others**: Please refer to [data.sampler](../lib/data/sampler.py) and [data.collate_fn](../lib/data/collate_fn.py).
> * **others**: Please refer to `evaluator_cfg`.
---
**Note**: All configuatrarion items will merged into [default.yaml](../config/default.yaml), and the current configuration is preferable.
**Note**:
- All the config items will be merged into [default.yaml](../config/default.yaml), and the current config is preferable.
- The output directory, which includes the log, checkpoint and summary files, is depended on the defined `dataset_name`, `model` and `save_name` settings, like `output/${dataset_name}/${model}/${save_name}`.
# Example
```yaml
@@ -102,7 +102,9 @@ evaluator_cfg:
sample_type: all_ordered # all indicates whole sequence used to test, while ordered means input sequence by its natural order; Other options: fixed_unordered
frames_all_limit: 720 # limit the number of sampled frames to prevent out of memory
metric: euc # cos
transform:
- type: BaseSilCuttingTransform
img_w: 64
loss_cfg:
- loss_term_weights: 1.0
@@ -128,6 +130,9 @@ model_cfg:
- M
- BC-256
- BC-256
# - M
# - BC-512
# - BC-512
type: Plain
SeparateFCs:
in_channels: 256
@@ -158,7 +163,7 @@ scheduler_cfg:
scheduler: MultiStepLR
trainer_cfg:
enable_float16: true # half_percesion float for memory reduction and speedup
fix_BN: false
fix_layers: false
log_iter: 100
restore_ckpt_strict: true
restore_hint: 0
@@ -176,6 +181,8 @@ trainer_cfg:
frames_num_min: 25 # min frames number for unfixed traing
sample_type: fixed_unordered # fixed control input frames number, unordered for controlling order of input tensor; Other options: unfixed_ordered or all_ordered
type: TripletSampler
transform:
- type: BaseSilCuttingTransform
img_w: 64
```
+86
View File
@@ -0,0 +1,86 @@
# How to Create Your Own Model
## Pipeline
![Pipeline](../assets/pipeline.png)
## A new model
If you want to design a new model, you need to write a class inherited from `BaseModel`, e.g, NewModel in newmodel.py:
```python
from ..base_model import BaseModel
class NewModel(BaseModel):
def __init__(self, cfgs, is_training):
super().__init__(cfgs, is_training)
def build_network(self, model_cfg):
self.encoder = ...
def forward(self, inputs):
ipts, labs, typs, viws, seqL = inputs
sils = ipts[0]
if len(sils.size()) == 4:
sils = sils.unsqueeze(2)
del ipts
n, s, c, h, w = sils.size()
embed_1, logits, embed = self.encoder(sils)
return {
'training_feat': {
'triplet': {'embeddings': embed_1, 'labels': labs},
'softmax': {'logits': logits, 'labels': labs}
},
'visual_summary': {
'image/sils': sils.view(n*s, 1, h, w)
},
'inference_feat': {
'embeddings': embed
}
}
```
In your model class, at least you need to implement `build_network()` and `forward()` functions. The first is used to build the netwroks, and it does not need `return value`. Another is used to calculate the features, the `return value` is fixed in dictionary format
> `training_feat` is for the loss computing, and it must be a `dict` object.
>
> `visual_summary` is for visualization, and it must be a `dict` object.
>
> `inference_feat` is for the inference, and it must be a `dict` object.
>
> `triplet` and `softmax` are the prefixes (or names) of the loss function.
>
> `embeddings`, `logits` and `labels` are the input arguments of the loss function.
More information should be seen in [base_model.py](../lib/modeling/base_model.py) and [loss_aggregator.py](../lib/modeling/loss_aggregator.py).
After finishing the model file, you have two steps left to do:
**Step 1**: Put your newmodel.py under `lib/modeling/models`.
**Step 2**: Specify the model name in a yaml file:
```yaml
model_cfg:
model: NewModel
param1: ...
param2: ...
param3: ...
```
## A new loss
If you want to write a new loss, you need to write a class inherited from `lib/modeling/losses`, like this
```python
from .base import BaseLoss
class NewLoss(BaseLoss):
def __init__(self, *args, **kwargs):
super(NewLoss, self).__init__(*args, **kargs)
@gather_and_scale_wrapper
def forward(self, embeddings, labels):
pass
```
Remember to use `gather_and_scale_wrapper` to wrap your forward function if your loss is computed by pairs like `triplet`. By this, we gather all features to one GPU card and scale the loss by the number of GPUs.
Then, put your loss in `lib/modeling/losses` so that you can use it in config file.
Moreover, refer to [loss_aggregator.py](../lib/modeling/loss_aggregator.py) to explore how does your defined loss work in the model.
+88
View File
@@ -0,0 +1,88 @@
# Advanced Usages
### Cross-Dataset Evalution
> You can conduct cross-dataset evalution by just modifying several arguments in your [data_cfg](../config/baseline.yaml#L1).
>
> Take [baseline.yaml](../config/baseline.yaml) as an example:
> ```yaml
> data_cfg:
> dataset_name: CASIA-B
> dataset_root: your_path
> dataset_partition: ./misc/partitions/CASIA-B_include_005.json
> num_workers: 1
> remove_no_gallery: false # Remove probe if no gallery for it
> test_dataset_name: CASIA-B
> ```
> Now, suppose we get the model trained on [CASIA-B](http://www.cbsr.ia.ac.cn/english/Gait%20Databases.asp), and then we want to test it on [OUMVLP](http://www.am.sanken.osaka-u.ac.jp/BiometricDB/GaitMVLP.html).
>
> We should alter the `dataset_root`, `dataset_partition` and `test_dataset_name`, just like:
> ```yaml
> data_cfg:
> dataset_name: CASIA-B
> dataset_root: your_OUMVLP_path
> dataset_partition: ./misc/partitions/OUMVLP.json
> num_workers: 1
> remove_no_gallery: false # Remove probe if no gallery for it
> test_dataset_name: OUMVLP
> ```
---
>
<!-- ### Identification Function
> Sometime, your test dataset may be neither the popular [CASIA-B](http://www.cbsr.ia.ac.cn/english/Gait%20Databases.asp) nor the largest [OUMVLP](http://www.am.sanken.osaka-u.ac.jp/BiometricDB/GaitMVLP.html). Meanwhile, you need to customize a special identification function to fit your dataset.
>
> * If your path structure is similar to [CASIA-B](http://www.cbsr.ia.ac.cn/english/Gait%20Databases.asp) (the 3-flod style: `id-type-view`), we recommand you to -->
### Data Augmentation
> In OpenGait, there is a basic transform class almost called by all the models, this is [BaseSilCuttingTransform](../lib/data/transform.py#L20), which is used to cut the input silhouettes.
>
> Accordingly, by referring to this implementation, you can easily customize the data agumentation in just two steps:
> * *Step1*: Define the transform function or class in [transform.py](../lib/data/transform.py), and make sure it callable. The style of [torchvision.transforms](https://pytorch.org/vision/stable/_modules/torchvision/transforms/transforms.html) is recommanded, and following shows a demo;
>> ```python
>> import torchvision.transforms as T
>> class demo1():
>> def __init__(self, args):
>> pass
>>
>> def __call__(self, seqs):
>> '''
>> seqs: with dimension of [sequence, height, width]
>> '''
>> pass
>> return seqs
>>
>> class demo2():
>> def __init__(self, args):
>> pass
>>
>> def __call__(self, seqs):
>> pass
>> return seqs
>>
>> def TransformDemo(base_args, demo1_args, demo2_args):
>> transform = T.Compose([
>> BaseSilCuttingTransform(**base_args),
>> demo1(args=demo1_args),
>> demo2(args=demo2_args)
>> ])
>> return transform
>> ```
> * *Step2*: Reset the [`transform`](../config/baseline.yaml#L100) arguments in your config file:
>> ```yaml
>> transform:
>> - type: TransformDemo
>> base_args: {'img_w': 64}
>> demo1_args: false
>> demo2_args: false
>> ```
### Visualization
> To learn how does the model work, sometimes, you need to visualize the intermediate result.
>
> For this purpose, we have defined a built-in instantiation of [`torch.utils.tensorboard.SummaryWriter`](https://pytorch.org/docs/stable/tensorboard.html), that is [`self.msg_mgr.writer`](../lib/utils/msg_manager.py#L24), to make sure you can log the middle information everywhere you want.
>
> Demo: if we want to visualize the output feature of [baseline's backbone](../lib/modeling/models/baseline.py#L27), we could just insert the following codes at [baseline.py#L28](../lib/modeling/models/baseline.py#L28):
>> ```python
>> summary_writer = self.msg_mgr.writer
>> if torch.distributed.get_rank() == 0 and self.training and self.iteration % 100==0:
>> summary_writer.add_video('outs', outs.mean(2).unsqueeze(2), self.iteration)
>> ```
> Note that this example requires the [`moviepy`](https://github.com/Zulko/moviepy) package, and hence you should run `pip install moviepy` first.
+5 -5
View File
@@ -52,14 +52,14 @@ class DataSet(tordata.Dataset):
def __getitem__(self, idx):
if not self.cache:
data_lst = self.__loader__(self.seqs_info[idx][-1])
data_list = self.__loader__(self.seqs_info[idx][-1])
elif self.seqs_data[idx] is None:
data_lst = self.__loader__(self.seqs_info[idx][-1])
self.seqs_data[idx] = data_lst
data_list = self.__loader__(self.seqs_info[idx][-1])
self.seqs_data[idx] = data_list
else:
data_lst = self.seqs_data[idx]
data_list = self.seqs_data[idx]
seq_info = self.seqs_info[idx]
return data_lst, seq_info
return data_list, seq_info
def __load_all_data(self):
for idx in range(len(self)):
+27 -6
View File
@@ -1,7 +1,28 @@
"""The plain backbone.
The plain backbone only contains the BasicConv2d, FocalConv2d and MaxPool2d and LeakyReLU layers.
"""
import torch.nn as nn
from ..modules import BasicConv2d, FocalConv2d
class Plain(nn.Module):
"""
The Plain backbone class.
An implicit LeakyRelu appended to each layer except maxPooling.
The kernel size, stride and padding of the first convolution layer are 5, 1, 2, the ones of other layers are 3, 1, 1.
Typical usage:
- BC-64: Basic conv2d with output channel 64. The input channel is the output channel of previous layer.
- M: nn.MaxPool2d(kernel_size=2, stride=2)].
- FC-128-1: Focal conv2d with output channel 64 and halving 1(divided to 2^1=2 parts).
Use it in your configuration file.
"""
def __init__(self, layers_cfg, in_channels=1):
super(Plain, self).__init__()
@@ -13,9 +34,11 @@ class Plain(nn.Module):
def forward(self, seqs):
out = self.feature(seqs)
return out
# torchvision/models/vgg.py
def make_layers(self):
"""
Reference: torchvision/models/vgg.py
"""
def get_layer(cfg, in_c, kernel_size, stride, padding):
cfg = cfg.split('-')
typ = cfg[0]
@@ -27,7 +50,8 @@ class Plain(nn.Module):
return BasicConv2d(in_c, out_c, kernel_size=kernel_size, stride=stride, padding=padding)
return FocalConv2d(in_c, out_c, kernel_size=kernel_size, stride=stride, padding=padding, halving=int(cfg[2]))
Layers = [get_layer(self.layers_cfg[0], self.in_channels, 5, 1, 2), nn.LeakyReLU(inplace=True)]
Layers = [get_layer(self.layers_cfg[0], self.in_channels,
5, 1, 2), nn.LeakyReLU(inplace=True)]
in_c = int(self.layers_cfg[0].split('-')[1])
for cfg in self.layers_cfg[1:]:
if cfg == 'M':
@@ -37,6 +61,3 @@ class Plain(nn.Module):
Layers += [conv2d, nn.LeakyReLU(inplace=True)]
in_c = int(cfg.split('-')[1])
return nn.Sequential(*Layers)
+84 -30
View File
@@ -1,3 +1,14 @@
"""The base model definition.
This module defines the abstract meta model class and base model class. In the base model,
we define the basic model functions, like get_loader, build_network, and run_train, etc.
The api of the base model is run_train and run_test, they are used in `lib/main.py`.
Typical usage:
BaseModel.run_train(model)
BaseModel.run_test(model)
"""
import torch
import numpy as np
import os.path as osp
@@ -13,7 +24,6 @@ from abc import abstractmethod
from . import backbones
from .loss_aggregator import LossAggregator
from modeling.modules import fix_BN
from data.transform import get_transform
from data.collate_fn import CollateFn
from data.dataset import DataSet
@@ -28,80 +38,97 @@ __all__ = ['BaseModel']
class MetaModel(metaclass=ABCMeta):
"""The necessary functions for the base model.
This class defines the necessary functions for the base model, in the base model, we have implemented them.
"""
@abstractmethod
def get_loader(self, data_cfg):
'''
Build your data Loader here.
Inputs: data_cfg, dict
Return: Loader
'''
"""Based on the given data_cfg, we get the data loader."""
raise NotImplementedError
@abstractmethod
def build_network(self, model_cfg):
'''
Build your Model here.
Inputs: model_cfg, dict
Return: Network, nn.Module(s)
'''
"""Build your network here."""
raise NotImplementedError
@abstractmethod
def init_parameters(self):
"""Initialize the parameters of your network."""
raise NotImplementedError
@abstractmethod
def get_optimizer(self, optimizer_cfg):
'''
Build your Optimizer here.
Inputs: optimizer_cfg, dict
Return: Optimizer, a optimizer object
'''
"""Based on the given optimizer_cfg, we get the optimizer."""
raise NotImplementedError
@abstractmethod
def get_scheduler(self, scheduler_cfg):
'''
Build your Scheduler.
Inputs: scheduler_cfg, dict
Optimizer, your optimizer
Return: Scheduler, a scheduler object
'''
"""Based on the given scheduler_cfg, we get the scheduler."""
raise NotImplementedError
@abstractmethod
def save_ckpt(self, iteration):
"""Save the checkpoint, including model parameter, optimizer and scheduler."""
raise NotImplementedError
@abstractmethod
def resume_ckpt(self, restore_hint):
"""Resume the model from the checkpoint, including model parameter, optimizer and scheduler."""
raise NotImplementedError
@abstractmethod
def inputs_pretreament(self, inputs):
"""Transform the input data based on transform setting."""
raise NotImplementedError
@abstractmethod
def train_step(self, loss_num) -> bool:
"""Do one training step."""
raise NotImplementedError
@abstractmethod
def inference(self):
"""Do inference (calculate features.)."""
raise NotImplementedError
@abstractmethod
def run_train(model):
"""Run a whole train schedule."""
raise NotImplementedError
@abstractmethod
def run_test(model):
"""Run a whole test schedule."""
raise NotImplementedError
class BaseModel(MetaModel, nn.Module):
"""Base model.
This class inherites the MetaModel class, and implements the basic model functions, like get_loader, build_network, etc.
Attributes:
msg_mgr: the massage manager.
cfgs: the configs.
iteration: the current iteration of the model.
engine_cfg: the configs of the engine(train or test).
save_path: the path to save the checkpoints.
"""
def __init__(self, cfgs, training):
"""Initialize the base model.
Complete the model initialization, including the data loader, the network, the optimizer, the scheduler, the loss.
Args:
cfgs:
All of the configs.
training:
Whether the model is in training mode.
"""
super(BaseModel, self).__init__()
self.msg_mgr = get_msg_mgr()
self.cfgs = cfgs
@@ -132,8 +159,6 @@ class BaseModel(MetaModel, nn.Module):
"cuda", self.device))
if training:
if cfgs['trainer_cfg']['fix_BN']:
fix_BN(self)
self.loss_aggregator = LossAggregator(cfgs['loss_cfg'])
self.optimizer = self.get_optimizer(self.cfgs['optimizer_cfg'])
self.scheduler = self.get_scheduler(cfgs['scheduler_cfg'])
@@ -142,7 +167,12 @@ class BaseModel(MetaModel, nn.Module):
if restore_hint != 0:
self.resume_ckpt(restore_hint)
if training:
if cfgs['trainer_cfg']['fix_BN']:
self.fix_BN()
def get_backbone(self, model_cfg):
"""Get the backbone of the model."""
def _get_backbone(backbone_cfg):
if is_dict(backbone_cfg):
Backbone = get_attr_from([backbones], backbone_cfg['type'])
@@ -266,7 +296,20 @@ class BaseModel(MetaModel, nn.Module):
"Error type for -Restore_Hint-, supported: int or string.")
self._load_ckpt(save_name)
def fix_BN(self):
for module in self.modules():
classname = module.__class__.__name__
if classname.find('BatchNorm') != -1:
module.eval()
def inputs_pretreament(self, inputs):
"""Conduct transforms on input data.
Args:
inputs: the input data.
Returns:
tuple: training data including inputs, labels, and some meta data.
"""
seqs_batch, labs_batch, typs_batch, vies_batch, seqL_batch = inputs
trf_cfgs = self.engine_cfg['transform']
seq_trfs = get_transform(trf_cfgs)
@@ -293,9 +336,13 @@ class BaseModel(MetaModel, nn.Module):
return ipts, labs, typs, vies, seqL
def train_step(self, loss_sum) -> bool:
'''
Conduct loss_sum.backward(), self.optimizer.step() and self.scheduler.step().
'''
"""Conduct loss_sum.backward(), self.optimizer.step() and self.scheduler.step().
Args:
loss_sum:The loss of the current batch.
Returns:
bool: True if the training is finished, False otherwise.
"""
self.optimizer.zero_grad()
if loss_sum <= 1e-9:
@@ -322,6 +369,13 @@ class BaseModel(MetaModel, nn.Module):
return True
def inference(self, rank):
"""Inference all the test data.
Args:
rank: the rank of the current process.Transform
Returns:
Odict: contains the inference results.
"""
total_size = len(self.test_loader)
if rank == 0:
pbar = tqdm(total=total_size, desc='Transforming')
@@ -355,9 +409,7 @@ class BaseModel(MetaModel, nn.Module):
@ staticmethod
def run_train(model):
'''
Accept the instance object(model) here, and then run the train loop handler.
'''
"""Accept the instance object(model) here, and then run the train loop."""
for inputs in model.train_loader:
ipts = model.inputs_pretreament(inputs)
with autocast(enabled=model.engine_cfg['enable_float16']):
@@ -390,6 +442,8 @@ class BaseModel(MetaModel, nn.Module):
@ staticmethod
def run_test(model):
"""Accept the instance object(model) here, and then run the test loop."""
rank = torch.distributed.get_rank()
with torch.no_grad():
info_dict = model.inference(rank)
+35 -5
View File
@@ -1,3 +1,5 @@
"""The loss aggregator."""
import torch
from . import losses
from utils import is_dict, get_attr_from, get_valid_args, is_tensor, get_ddp_module
@@ -6,18 +8,48 @@ from utils import get_msg_mgr
class LossAggregator():
"""The loss aggregator.
This class is used to aggregate the losses.
For example, if you have two losses, one is triplet loss, the other is cross entropy loss,
you can aggregate them as follows:
loss_num = tripley_loss + cross_entropy_loss
Attributes:
losses: A dict of losses.
"""
def __init__(self, loss_cfg) -> None:
"""
Initialize the loss aggregator.
Args:
loss_cfg: Config of losses. List for multiple losses.
"""
self.losses = {loss_cfg['log_prefix']: self._build_loss_(loss_cfg)} if is_dict(loss_cfg) \
else {cfg['log_prefix']: self._build_loss_(cfg) for cfg in loss_cfg}
def _build_loss_(self, loss_cfg):
"""Build the losses from loss_cfg.
Args:
loss_cfg: Config of loss.
"""
Loss = get_attr_from([losses], loss_cfg['type'])
valid_loss_arg = get_valid_args(
Loss, loss_cfg, ['type', 'pair_based_loss'])
loss = get_ddp_module(Loss(**valid_loss_arg))
Loss, loss_cfg, ['type', 'gather_and_scale'])
loss = get_ddp_module(Loss(**valid_loss_arg).cuda())
return loss
def __call__(self, training_feats):
"""Compute the sum of all losses.
The input is a dict of features. The key is the name of loss and the value is the feature and label. If the key not in
built losses and the value is torch.Tensor, then it is the computed loss to be added loss_sum.
Args:
training_feats: A dict of features. The same as the output["training_feat"] of the model.
"""
loss_sum = .0
loss_info = Odict()
@@ -28,14 +60,12 @@ class LossAggregator():
for name, value in info.items():
loss_info['scalar/%s/%s' % (k, name)] = value
loss = loss.mean() * loss_func.loss_term_weights
if loss_func.pair_based_loss:
loss = loss * torch.distributed.get_world_size()
loss_sum += loss
else:
if isinstance(v, dict):
raise ValueError(
"The key %s in -Trainng-Feat- should be stated as the log_prefix of a certain loss defined in your loss_cfg."
"The key %s in -Trainng-Feat- should be stated as the log_prefix of a certain loss defined in your loss_cfg."%v
)
elif is_tensor(v):
_ = v.mean()
+49 -8
View File
@@ -1,13 +1,54 @@
from ctypes import ArgumentError
import torch.nn as nn
import torch
from utils import Odict
import functools
from utils import ddp_all_gather
class BasicLoss(nn.Module):
def __init__(self, loss_term_weights=1.0):
super(BasicLoss, self).__init__()
self.loss_term_weights = loss_term_weights
self.pair_based_loss = True
self.info = Odict()
def gather_and_scale_wrapper(func):
"""Internal wrapper: gather the input from multple cards to one card, and scale the loss by the number of cards.
"""
@functools.wraps(func)
def inner(*args, **kwds):
try:
for k, v in kwds.items():
kwds[k] = ddp_all_gather(v)
loss, loss_info = func(*args, **kwds)
loss *= torch.distributed.get_world_size()
return loss, loss_info
except:
raise ArgumentError
return inner
class BaseLoss(nn.Module):
"""
Base class for all losses.
Your loss should also subclass this class.
Attribute:
loss_term_weights: the weight of the loss.
info: the loss info.
"""
loss_term_weights = 1.0
info = Odict()
def forward(self, logits, labels):
raise NotImplementedError
"""
The default forward function.
This function should be overridden by the subclass.
Args:
logits: the logits of the model.
labels: the labels of the data.
Returns:
tuple of loss and info.
"""
return .0, self.info
+3 -4
View File
@@ -1,10 +1,10 @@
import torch
import torch.nn.functional as F
from .base import BasicLoss
from .base import BaseLoss
class CrossEntropyLoss(BasicLoss):
class CrossEntropyLoss(BaseLoss):
def __init__(self, scale=2**4, label_smooth=True, eps=0.1, loss_term_weights=1.0, log_accuracy=False):
super(CrossEntropyLoss, self).__init__()
self.scale = scale
@@ -13,7 +13,6 @@ class CrossEntropyLoss(BasicLoss):
self.log_accuracy = log_accuracy
self.loss_term_weights = loss_term_weights
self.pair_based_loss = False
def forward(self, logits, labels):
"""
@@ -26,7 +25,7 @@ class CrossEntropyLoss(BasicLoss):
one_hot_labels = self.label2one_hot(
labels, c).unsqueeze(0).repeat(p, 1, 1) # [p, n, c]
loss = self.compute_loss(log_preds, one_hot_labels)
self.info.update({'loss': loss})
self.info.update({'loss': loss.detach().clone()})
if self.log_accuracy:
pred = logits.argmax(dim=-1) # [p, n]
accu = (pred == labels.unsqueeze(0)).float().mean()
+7 -10
View File
@@ -1,22 +1,19 @@
import torch
import torch.nn.functional as F
from .base import BasicLoss
from utils import ddp_all_gather
from .base import BaseLoss, gather_and_scale_wrapper
class TripletLoss(BasicLoss):
class TripletLoss(BaseLoss):
def __init__(self, margin, loss_term_weights=1.0):
super(TripletLoss, self).__init__()
self.margin = margin
self.loss_term_weights = loss_term_weights
self.pair_based_loss = True
@gather_and_scale_wrapper
def forward(self, embeddings, labels):
# embeddings: [n, p, c], label: [n]
embeddings = ddp_all_gather(embeddings)
labels = ddp_all_gather(labels)
embeddings = embeddings.permute(
1, 0, 2).contiguous() # [n, p, c] -> [p, n, c]
embeddings = embeddings.float()
@@ -32,10 +29,10 @@ class TripletLoss(BasicLoss):
loss_avg, loss_num = self.AvgNonZeroReducer(loss)
self.info.update({
'loss': loss_avg,
'hard_loss': hard_loss,
'loss_num': loss_num,
'mean_dist': mean_dist})
'loss': loss_avg.detach().clone(),
'hard_loss': hard_loss.detach().clone(),
'loss_num': loss_num.detach().clone(),
'mean_dist': mean_dist.detach().clone()})
return loss_avg, self.info
+66 -24
View File
@@ -63,8 +63,8 @@ class GeMHPP(nn.Module):
class GaitGL(BaseModel):
"""
Title: Gait Recognition via Effective Global-Local Feature Representation and Local Temporal Aggregation
ICCV2021: https://openaccess.thecvf.com/content/ICCV2021/papers/Lin_Gait_Recognition_via_Effective_Global-Local_Feature_Representation_and_Local_Temporal_ICCV_2021_paper.pdf
GaitGL: Gait Recognition via Effective Global-Local Feature Representation and Local Temporal Aggregation
Arxiv : https://arxiv.org/pdf/2011.01461.pdf
"""
def __init__(self, *args, **kargs):
@@ -73,31 +73,71 @@ class GaitGL(BaseModel):
def build_network(self, model_cfg):
in_c = model_cfg['channels']
class_num = model_cfg['class_num']
dataset_name = self.cfgs['data_cfg']['dataset_name']
# For CASIA-B
self.conv3d = nn.Sequential(
BasicConv3d(1, in_c[0], kernel_size=(3, 3, 3),
stride=(1, 1, 1), padding=(1, 1, 1)),
nn.LeakyReLU(inplace=True)
)
self.LTA = nn.Sequential(
BasicConv3d(in_c[0], in_c[0], kernel_size=(
3, 1, 1), stride=(3, 1, 1), padding=(0, 0, 0)),
nn.LeakyReLU(inplace=True)
)
if dataset_name == 'OUMVLP':
# For OUMVLP
self.conv3d = nn.Sequential(
BasicConv3d(1, in_c[0], kernel_size=(3, 3, 3),
stride=(1, 1, 1), padding=(1, 1, 1)),
nn.LeakyReLU(inplace=True),
BasicConv3d(in_c[0], in_c[0], kernel_size=(3, 3, 3),
stride=(1, 1, 1), padding=(1, 1, 1)),
nn.LeakyReLU(inplace=True),
)
self.LTA = nn.Sequential(
BasicConv3d(in_c[0], in_c[0], kernel_size=(
3, 1, 1), stride=(3, 1, 1), padding=(0, 0, 0)),
nn.LeakyReLU(inplace=True)
)
self.GLConvA0 = GLConv(in_c[0], in_c[1], halving=3, fm_sign=False, kernel_size=(
3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1))
self.MaxPool0 = nn.MaxPool3d(kernel_size=(1, 2, 2), stride=(1, 2, 2))
self.GLConvA0 = nn.Sequential(
GLConv(in_c[0], in_c[1], halving=1, fm_sign=False, kernel_size=(
3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1)),
GLConv(in_c[1], in_c[1], halving=1, fm_sign=False, kernel_size=(
3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1)),
)
self.MaxPool0 = nn.MaxPool3d(
kernel_size=(1, 2, 2), stride=(1, 2, 2))
self.GLConvA1 = GLConv(in_c[1], in_c[2], halving=3, fm_sign=False, kernel_size=(
3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1))
self.GLConvB2 = GLConv(in_c[2], in_c[2], halving=3, fm_sign=True, kernel_size=(
3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1))
self.GLConvA1 = nn.Sequential(
GLConv(in_c[1], in_c[2], halving=1, fm_sign=False, kernel_size=(
3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1)),
GLConv(in_c[2], in_c[2], halving=1, fm_sign=False, kernel_size=(
3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1)),
)
self.GLConvB2 = nn.Sequential(
GLConv(in_c[2], in_c[3], halving=1, fm_sign=False, kernel_size=(
3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1)),
GLConv(in_c[3], in_c[3], halving=1, fm_sign=True, kernel_size=(
3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1)),
)
else:
# For CASIA-B or other unstated datasets.
self.conv3d = nn.Sequential(
BasicConv3d(1, in_c[0], kernel_size=(3, 3, 3),
stride=(1, 1, 1), padding=(1, 1, 1)),
nn.LeakyReLU(inplace=True)
)
self.LTA = nn.Sequential(
BasicConv3d(in_c[0], in_c[0], kernel_size=(
3, 1, 1), stride=(3, 1, 1), padding=(0, 0, 0)),
nn.LeakyReLU(inplace=True)
)
self.Head0 = SeparateFCs(64, in_c[2], in_c[2])
self.Bn = nn.BatchNorm1d(in_c[2])
self.Head1 = SeparateFCs(64, in_c[2], class_num)
self.GLConvA0 = GLConv(in_c[0], in_c[1], halving=3, fm_sign=False, kernel_size=(
3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1))
self.MaxPool0 = nn.MaxPool3d(
kernel_size=(1, 2, 2), stride=(1, 2, 2))
self.GLConvA1 = GLConv(in_c[1], in_c[2], halving=3, fm_sign=False, kernel_size=(
3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1))
self.GLConvB2 = GLConv(in_c[2], in_c[2], halving=3, fm_sign=True, kernel_size=(
3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1))
self.Head0 = SeparateFCs(64, in_c[-1], in_c[-1])
self.Bn = nn.BatchNorm1d(in_c[-1])
self.Head1 = SeparateFCs(64, in_c[-1], class_num)
self.TP = PackSequenceWrapper(torch.max)
self.HPP = GeMHPP()
@@ -105,7 +145,9 @@ class GaitGL(BaseModel):
def forward(self, inputs):
ipts, labs, _, _, seqL = inputs
seqL = None if not self.training else seqL
if not self.training and len(labs) != 1:
raise ValueError(
'The input size of each GPU must be 1 in testing mode, but got {}!'.format(len(labs)))
sils = ipts[0].unsqueeze(1)
del ipts
n, _, s, h, w = sils.size()
-7
View File
@@ -191,10 +191,3 @@ def RmBN2dAffine(model):
if isinstance(m, nn.BatchNorm2d):
m.weight.requires_grad = False
m.bias.requires_grad = False
def fix_BN(model):
for module in model.modules():
classname = module.__class__.__name__
if classname.find('BatchNorm2d') != -1:
module.eval()
+1 -1
View File
@@ -1,7 +1,7 @@
from .common import get_ddp_module, ddp_all_gather
from .common import Odict, Ntuple
from .common import get_valid_args
from .common import is_list_or_tuple, is_str, is_list, is_dict, is_tensor, is_array, config_loader, init_seeds, handler, params_count
from .common import is_list_or_tuple, is_bool, is_str, is_list, is_dict, is_tensor, is_array, config_loader, init_seeds, handler, params_count
from .common import ts2np, ts2var, np2var, list2var
from .common import mkdir, clones
from .common import MergeCfgsDict
+4
View File
@@ -74,6 +74,10 @@ def is_list_or_tuple(x):
return isinstance(x, (list, tuple))
def is_bool(x):
return isinstance(x, bool)
def is_str(x):
return isinstance(x, str)
+1
View File
@@ -0,0 +1 @@
kill $(ps aux | grep main.py | grep -v grep | awk '{print $2}')
File diff suppressed because it is too large Load Diff
+59
View File
@@ -0,0 +1,59 @@
import os
import shutil
from tqdm import tqdm
import argparse
parser = argparse.ArgumentParser(description='Test')
parser.add_argument('--input_path', default='/home1/data/OUMVLP_raw', type=str,
help='Root path of raw dataset.')
parser.add_argument('--output_path', default='/home1/data/OUMVLP_rearranged', type=str,
help='Root path for output.')
opt = parser.parse_args()
INPUT_PATH = opt.input_path
OUTPUT_PATH = opt.output_path
def mv_dir(src, dst):
shutil.copytree(src, dst)
print(src, dst)
sils_name_list = os.listdir(INPUT_PATH)
name_space = 'Silhouette_'
views = sorted(list(
set([each.replace(name_space, '').split('-')[0] for each in sils_name_list])))
seqs = sorted(list(
set([each.replace(name_space, '').split('-')[1] for each in sils_name_list])))
ids = list()
for each in sils_name_list:
ids.extend(os.listdir(os.path.join(INPUT_PATH, each)))
progress = tqdm(total=len(set(ids)))
results = list()
pid = 0
for _id in sorted(set(ids)):
progress.update(1)
for _view in views:
for _seq in seqs:
seq_info = [_id, _seq, _view]
name = name_space + _view + '-' + _seq + '/' + _id
src = os.path.join(INPUT_PATH, name)
dst = os.path.join(OUTPUT_PATH, *seq_info)
if os.path.exists(src):
try:
if os.path.exists(dst):
pass
else:
os.makedirs(dst)
for subfile in os.listdir(src):
os.symlink(os.path.join(src, subfile),
os.path.join(dst, subfile))
except OSError as err:
print(err)
+18 -3
View File
@@ -1,5 +1,6 @@
# # **************** For CASIA-B ****************
# # Baseline
# CUDA_VISIBLE_DEVICES=0,1 python -m torch.distributed.launch --nproc_per_node=2 lib/main.py --cfgs ./config/baseline.yaml --phase test
CUDA_VISIBLE_DEVICES=0,1 python -m torch.distributed.launch --nproc_per_node=2 lib/main.py --cfgs ./config/baseline.yaml --phase test
# # GaitSet
# CUDA_VISIBLE_DEVICES=0,1 python -m torch.distributed.launch --nproc_per_node=2 lib/main.py --cfgs ./config/gaitset.yaml --phase test
@@ -8,10 +9,24 @@
# CUDA_VISIBLE_DEVICES=0,1 python -m torch.distributed.launch --nproc_per_node=2 lib/main.py --cfgs ./config/gaitpart.yaml --phase test
# GaitGL
# CUDA_VISIBLE_DEVICES=0,1,2,3 python -m torch.distributed.launch --master_port 12345 --nproc_per_node=4 lib/main.py --cfgs ./config/gaitgl.yaml --iter 80000 --phase test
# CUDA_VISIBLE_DEVICES=0,1,2,3 python -m torch.distributed.launch --master_port 12345 --nproc_per_node=4 lib/main.py --cfgs ./config/gaitgl.yaml --phase test
# # GLN
# # Phase 1
CUDA_VISIBLE_DEVICES=3,4 python -m torch.distributed.launch --master_port 12345 --nproc_per_node=2 lib/main.py --cfgs ./config/gln/gln_phase1.yaml --phase test
# CUDA_VISIBLE_DEVICES=3,4 python -m torch.distributed.launch --master_port 12345 --nproc_per_node=2 lib/main.py --cfgs ./config/gln/gln_phase1.yaml --phase test
# # Phase 2
# CUDA_VISIBLE_DEVICES=2,5 python -m torch.distributed.launch --nproc_per_node=2 lib/main.py --cfgs ./config/gln/gln_phase2.yaml --phase test
# # **************** For OUMVLP ****************
# # Baseline
# CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 python -m torch.distributed.launch --nproc_per_node=8 lib/main.py --cfgs ./config/baseline_OUMVLP.yaml --phase test
# # GaitSet
# CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 python -m torch.distributed.launch --nproc_per_node=8 lib/main.py --cfgs ./config/gaitset_OUMVLP.yaml --phase test
# # GaitPart
# CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 python -m torch.distributed.launch --nproc_per_node=8 lib/main.py --cfgs ./config/gaitpart_OUMVLP.yaml --phase test
# GaitGL
# CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 python -m torch.distributed.launch --nproc_per_node=8 lib/main.py --cfgs ./config/gaitgl_OUMVLP.yaml --phase test
+19 -4
View File
@@ -1,5 +1,6 @@
# # **************** For CASIA-B ****************
# # Baseline
# CUDA_VISIBLE_DEVICES=0,1 python -m torch.distributed.launch --nproc_per_node=2 lib/main.py --cfgs ./config/baseline.yaml --phase train
CUDA_VISIBLE_DEVICES=0,1 python -m torch.distributed.launch --nproc_per_node=2 lib/main.py --cfgs ./config/baseline.yaml --phase train
# # GaitSet
# CUDA_VISIBLE_DEVICES=0,1 python -m torch.distributed.launch --nproc_per_node=2 lib/main.py --cfgs ./config/gaitset.yaml --phase train
@@ -8,10 +9,24 @@
# CUDA_VISIBLE_DEVICES=0,1 python -m torch.distributed.launch --nproc_per_node=2 lib/main.py --cfgs ./config/gaitpart.yaml --phase train
# GaitGL
#CUDA_VISIBLE_DEVICES=0,1,2,3 python -m torch.distributed.launch --nproc_per_node=4 lib/main.py --cfgs ./config/gaitgl.yaml --phase train
# CUDA_VISIBLE_DEVICES=0,1,2,3 python -m torch.distributed.launch --nproc_per_node=4 lib/main.py --cfgs ./config/gaitgl.yaml --phase train
# # GLN
# # Phase 1
CUDA_VISIBLE_DEVICES=2,5,6,7 python -m torch.distributed.launch --nproc_per_node=4 lib/main.py --cfgs ./config/gln/gln_phase1.yaml --phase train
# CUDA_VISIBLE_DEVICES=2,5,6,7 python -m torch.distributed.launch --nproc_per_node=4 lib/main.py --cfgs ./config/gln/gln_phase1.yaml --phase train
# # Phase 2
CUDA_VISIBLE_DEVICES=2,5,6,7 python -m torch.distributed.launch --nproc_per_node=4 lib/main.py --cfgs ./config/gln/gln_phase2.yaml --phase train
# CUDA_VISIBLE_DEVICES=2,5,6,7 python -m torch.distributed.launch --nproc_per_node=4 lib/main.py --cfgs ./config/gln/gln_phase2.yaml --phase train
# # **************** For OUMVLP ****************
# # Baseline
# CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 python -m torch.distributed.launch --nproc_per_node=8 lib/main.py --cfgs ./config/baseline_OUMVLP.yaml --phase train
# # GaitSet
# CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 python -m torch.distributed.launch --nproc_per_node=8 lib/main.py --cfgs ./config/gaitset_OUMVLP.yaml --phase train
# # GaitPart
# CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 python -m torch.distributed.launch --nproc_per_node=8 lib/main.py --cfgs ./config/gaitpart_OUMVLP.yaml --phase train
# GaitGL
# CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 python -m torch.distributed.launch --nproc_per_node=8 lib/main.py --cfgs ./config/gaitgl_OUMVLP.yaml --phase train