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:
@@ -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/).
|
||||
@@ -0,0 +1,188 @@
|
||||
# Configuration item
|
||||
|
||||
### data_cfg
|
||||
* Data configuration
|
||||
>
|
||||
> * Args
|
||||
> * 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.
|
||||
> * cache: If `True`, load all data to memory during buiding dataset.
|
||||
> * test_dataset_name: The name of test dataset.
|
||||
----
|
||||
|
||||
### loss_cfg
|
||||
* Loss function
|
||||
> * Args
|
||||
> * type: Loss function type, support `TripletLoss` and `CrossEntropyLoss`.
|
||||
> * loss_term_weights: loss weight.
|
||||
> * log_prefix: the prefix of loss log.
|
||||
|
||||
----
|
||||
### optimizer_cfg
|
||||
* Optimizer
|
||||
> * Args
|
||||
> * 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`.
|
||||
----
|
||||
### 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 the [Training Configuration File of Corresponding Model](../config).
|
||||
----
|
||||
### evaluator_cfg
|
||||
* Evaluator configuration
|
||||
> * Args
|
||||
> * 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`.
|
||||
> - 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: `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 out the black pixels on both sides horizontally.
|
||||
> * metric: `euc` or `cos`, generally, `euc` performs better.
|
||||
|
||||
----
|
||||
### trainer_cfg
|
||||
* Trainer configuration
|
||||
> * Args
|
||||
> * fix_BN: If `True`, we fix the weight of all `BatchNorm` layers.
|
||||
> * 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 synchronously.
|
||||
> * total_iter: The total training iterations, `int` values.
|
||||
> * sampler:
|
||||
> - 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]* 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`.
|
||||
---
|
||||
**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
|
||||
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
|
||||
|
||||
evaluator_cfg:
|
||||
enable_float16: true
|
||||
restore_ckpt_strict: true
|
||||
restore_hint: 60000
|
||||
save_name: Baseline
|
||||
eval_func: identification
|
||||
sampler:
|
||||
batch_shuffle: false
|
||||
batch_size: 16
|
||||
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
|
||||
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-64
|
||||
- BC-64
|
||||
- M
|
||||
- BC-128
|
||||
- BC-128
|
||||
- M
|
||||
- BC-256
|
||||
- BC-256
|
||||
# - M
|
||||
# - BC-512
|
||||
# - BC-512
|
||||
type: Plain
|
||||
SeparateFCs:
|
||||
in_channels: 256
|
||||
out_channels: 256
|
||||
parts_num: 31
|
||||
SeparateBNNecks:
|
||||
class_num: 74
|
||||
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
|
||||
- 20000
|
||||
- 40000
|
||||
scheduler: MultiStepLR
|
||||
trainer_cfg:
|
||||
enable_float16: true # half_percesion float for memory reduction and speedup
|
||||
fix_layers: false
|
||||
log_iter: 100
|
||||
restore_ckpt_strict: true
|
||||
restore_hint: 0
|
||||
save_iter: 10000
|
||||
save_name: Baseline
|
||||
sync_BN: true
|
||||
total_iter: 60000
|
||||
sampler:
|
||||
batch_shuffle: true
|
||||
batch_size:
|
||||
- 8 # 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: 64
|
||||
|
||||
```
|
||||
@@ -0,0 +1,86 @@
|
||||
# How to Create Your Own Model
|
||||
## Pipeline
|
||||

|
||||
|
||||
## 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.
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user