Skip to content

Commit

Permalink
update models, schedulers and config files
Browse files Browse the repository at this point in the history
  • Loading branch information
x.ji committed Jan 10, 2021
1 parent 8e2d957 commit a17dd49
Show file tree
Hide file tree
Showing 27 changed files with 1,657 additions and 482 deletions.
31 changes: 17 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ List of functions
- Warm up cosine annealing learning rate
- Random erasing augmentation
- Cutout augmentation
- Batch Drop Block
- Batch Drop Block and Batch Erasing
- Label smoothing(Cross Entropy loss)
- Triplet loss
- Multi-Simulatity loss
Expand All @@ -29,19 +29,20 @@ Inplemented networks:
- MGN [[link]](https://arxiv.org/abs/1804.01438)
- Bag of tricks [[link]](http://openaccess.thecvf.com/content_CVPRW_2019/papers/TRMTMCT/Luo_Bag_of_Tricks_and_a_Strong_Baseline_for_Deep_Person_CVPRW_2019_paper.pdf)
- OSNet [[link]](https://arxiv.org/abs/1905.00953)
- Batch Drop Block for Person ReID [[link]](https://arxiv.org/abs/1811.07130)
- Batch Drop Block(BDB) for Person ReID [[link]](https://arxiv.org/abs/1811.07130)


## Getting Started
The designed architecture is concise and easy explicable, where the file engine.py defines the train/ test process and main.py controls the overall epochs, and the folders model, loss, optimizer including respective parts of neural network.
The designed code architecture is concise and easy explicable, where the file engine.py defines the train/ test process and main.py controls the overall epochs, and the folders model, loss, optimizer including respective parts of neural network.

The user-friendly command-line module argparse helps us indicate different datasets, networks, loss functions, and tricks as we need,
the detailed options/configurations are described in the bottom of this page.

If you don't have any dataset yet, run `git clone https://github.com/jixunbo/ReIDataset.git` to download Market-1501, DukeMTMC, and MOT17.

To inplement Multi-Parts Multi-Channels Network with Multi-Similarity loss, run
`python [path to repo]/main.py --datadir [path to datasets] --data_train DukeMTMC --data_test DukeMTMC --model MCMP_n --batchid 8 --batchimage 8 --batchtest 32 --test_every 10 --epochs 120 --save '' --decay_type step_50_80_110 --loss 0.5*CrossEntropy+0.5*MSLoss --margin 0.75 --nGPU 1 --lr 3.5e-4 --optimizer ADAM --random_erasing --w_cosine_annealing --if_labelsmooth --feats 512`

`python [path to repo]/main.py --datadir [path to datasets] --data_train dukemtmc --data_test dukemtmc --model MCMP_n --batchid 8 --batchimage 6 --batchtest 32 --test_every 20 --epochs 110 --loss 0.5*CrossEntropy+0.5*MSLoss --margin 0.7 --nGPU 1 --lr 6e-4 --optimizer ADAM --random_erasing --feats 512 --pool avg --save '' --if_labelsmooth --w_cosine_annealing`

Also, using pre-defined config file
`python [path to repo]/main.py --config [path to repo]/mcmp_config.yaml --save ''`
Expand All @@ -52,29 +53,29 @@ Note that, the option '--datadir' is the dataset root, which contains folder Mar

'--data_train' and '--data_test' specify the name of train/test dataset, which we can train on one dataset but test on another dataset.

'--batchid 6' and '--batchimage 8' indicate that each batch contrains 6 persons, each person has 8 different images, totally 48 images.
'--batchid 8' and '--batchimage 6' indicate that each batch contrains 8 persons, each person has 6 different images, totally 48 images.

'--epochs' is the epochs we'd like to train, while '--test_every 10' means evaluation will be excuted in every 10 epochs, the parameters of network and optimizer are updated after every every evaluation.

Actually, for the MCMP model we have two kinds of backbone, MCMP_r we use ResNet 50 as backbone, while MCMP_n is OSNet, OSNet contrains much less parameters but could achieve a little bit better performance than ResNet50.
Actually, for the MCMP model we have two kinds of backbone, MCMP_r we use ResNet 50 or ResNet 50 IBN as backbone, while MCMP_n is OSNet, OSNet contrains much less parameters but could achieve a little bit better performance than ResNet50.

If you would like to re-inplement Bag of Tricks, run

`python [path to repo]/main.py --datadir [path to datasets] --data_train Market1501 --data_test Market1501 --model ResNet50 --batchid 16 --batchimage 4 --batchtest 32 --test_every 10 --epochs 120 --save '' --decay_type step_40_70 --loss 0.5*CrossEntropy+0.5*Triplet --margin 0.3 --nGPU 1 --lr 3.5e-4 --optimizer ADAM --random_erasing --warmup 'constant' --if_labelsmooth`
`python [path to repo]/main.py --datadir [path to datasets] --data_train Market1501 --data_test Market1501 --model ResNet50 --batchid 16 --batchimage 4 --batchtest 32 --test_every 10 --epochs 120 --save '' --decay_type step_40_70 --loss 0.5*CrossEntropy+0.5*Triplet --margin 0.3 --nGPU 1 --lr 3.5e-4 --optimizer ADAM --random_erasing --warmup 'linear' --if_labelsmooth`

or

`python [path to repo]/main.py --config [path to repo]/bag_of_tricks_config.yaml --save`

If you would like to re-inplement PCB, run
If you would like to re-inplement PCB with powerful training tricks, run

`python [path to repo]/main.py --datadir [path to datasets] --data_train Market1501 --data_test Market1501 --model PCB --batchid 8 --batchimage 8 --batchtest 32 --test_every 10 --epochs 120 --save '' --decay_type step_50_80_110 --loss 0.5*CrossEntropy+0.5*MSLoss --margin 0.75 --nGPU 1 --lr 5e-3 --optimizer ADAM --random_erasing --warmup 'constant' --if_labelsmooth --bnneck --parts 3`
`python [path to repo]/main.py --datadir [path to datasets] --data_train Market1501 --data_test Market1501 --model PCB --batchid 8 --batchimage 8 --batchtest 32 --test_every 10 --epochs 120 --save '' --decay_type step_50_80_110 --loss 0.5*CrossEntropy+0.5*MSLoss --margin 0.7 --nGPU 1 --lr 5e-3 --optimizer ADAM --random_erasing --warmup 'linear' --if_labelsmooth --bnneck --parts 3`

Note that, the option '--parts' is used to set the number of stripes to be devided, original paper set 6.

And also, for MGN model run

`python [path to repo]/main.py --datadir [path to datasets] --data_train Market1501 --data_test Market1501 --model MGN --batchid 16 --batchimage 4 --batchtest 32 --test_every 10 --epochs 120 --save '' --decay_type step_50_80_110 --loss 0.5*CrossEntropy+0.5*Triplet --margin 1.2 --nGPU 1 --lr 2e-4 --optimizer ADAM --random_erasing --warmup 'constant' --if_labelsmooth`
`python [path to repo]/main.py --datadir [path to datasets] --data_train Market1501 --data_test Market1501 --model MGN --batchid 16 --batchimage 4 --batchtest 32 --test_every 10 --epochs 120 --save '' --decay_type step_50_80_110 --loss 0.5*CrossEntropy+0.5*Triplet --margin 1.2 --nGPU 1 --lr 2e-4 --optimizer ADAM --random_erasing --warmup 'linear' --if_labelsmooth`

If you have pretrained model and config file, run

Expand Down Expand Up @@ -106,8 +107,8 @@ If you are hard-core player ^ ^ and you'd like to try different models or option
### Results
| Model | Market1501 | DukeMTMC-reID |
| --- | -- | -- |
| MCMP_n | 96.3 (90.9) | 91.3 (82.6) |
| MCMP_r | 95.6 (90.1) | 90.2 (81.5) |
| MCMP_n | 96.3 (91.3) | 92.0 (83.2) |
| MCMP_r | 95.8 (90.4) | 90.5 (82.0) |
| BoT | 94.2 (85.4) | 86.7 (75.8) |
| PCB | 95.1 (86.3) | 87.6 (76.6) |
| MGN | 94.7 (87.5) | 88.7 (79.4) |
Expand Down Expand Up @@ -155,7 +156,7 @@ Additionally, the evaluation metric method is the same as bag of tricks [repo](h

'--bnneck', action='store_true', if raise, use BNNeck, only for ResNet and PCB.

'--drop_block', action='store_true', if raise, use Batch Drop Block.
'--drop_block', action='store_true', if raise, use Batch Drop Block, and '--h_ratio 0.3 and --w_ratio 1.0' indicate the erased region on the feature maps.

'--pool', type=str, default='avg', choose pooling method, options: avg, max.

Expand All @@ -171,7 +172,9 @@ Additionally, the evaluation metric method is the same as bag of tricks [repo](h

'--gamma', type=float, default=0.1,learning rate decay factor for step decay.

'--warmup', type=str, default='none', learning rate warmup method, options: linear, constant, none.
'--warmup', type=str, default='constant', learning rate warmup method, options: linear, constant.

'--w_cosine_annealing', action='store_true', if raise, use warm up cosine annealing learning rate scheduler.

'--pcb_different_lr', type=str, default='True', if 'True', use different lr only for PCB, if lr is 5e-3, then lr for classifier is 5e-3, lr for other part is 5e-4.

Expand Down
26 changes: 21 additions & 5 deletions data_v2/datamanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@
from .transforms import build_transforms
from .datasets import init_image_dataset, init_video_dataset

"""
Speed Up Dataloader
"""
from prefetch_generator import BackgroundGenerator

class DataloaderX(torch.utils.data.DataLoader):

def __iter__(self):
return BackgroundGenerator(super().__iter__())


class DataManager(object):
r"""Base data manager.
Expand Down Expand Up @@ -126,7 +137,9 @@ def __init__(self, args):
targets = args.data_test.lower()
height = args.height
width = args.width
transforms = ['random_flip', 'random_crop']
transforms = ['random_flip',
'random_crop'
]
norm_mean = [0.485, 0.456, 0.406]
norm_std = [0.229, 0.224, 0.225]
use_gpu = not args.cpu
Expand All @@ -137,7 +150,7 @@ def __init__(self, args):
batch_size_test = args.batchtest
workers = args.nThread
train_sampler = 'random'
cuhk03_labeled = True
cuhk03_labeled = False
cuhk03_classic_split = False
market1501_500k = False

Expand Down Expand Up @@ -177,7 +190,8 @@ def __init__(self, args):
num_instances=num_instances
)

self.train_loader = torch.utils.data.DataLoader(
# self.train_loader = torch.utils.data.DataLoader(
self.train_loader = DataloaderX(
trainset,
sampler=train_sampler,
batch_size=batch_size_train,
Expand Down Expand Up @@ -206,7 +220,8 @@ def __init__(self, args):
cuhk03_classic_split=cuhk03_classic_split,
market1501_500k=market1501_500k
)
self.testloader[name]['query'] = torch.utils.data.DataLoader(
# self.testloader[name]['query'] = torch.utils.data.DataLoader(
self.testloader[name]['query'] = DataloaderX(
queryset,
batch_size=batch_size_test,
shuffle=False,
Expand All @@ -228,7 +243,8 @@ def __init__(self, args):
cuhk03_classic_split=cuhk03_classic_split,
market1501_500k=market1501_500k
)
self.testloader[name]['gallery'] = torch.utils.data.DataLoader(
# self.testloader[name]['gallery'] = torch.utils.data.DataLoader(
self.testloader[name]['gallery'] = DataloaderX(
galleryset,
batch_size=batch_size_test,
shuffle=False,
Expand Down
1 change: 1 addition & 0 deletions data_v2/datasets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
'cuhk03': CUHK03,
'cuhk03_detected': CUHK03_Detected,
'cuhk03_labeled': CUHK03_Labeled,
'cuhk03_splited': CUHK03_splited,
'dukemtmc': DukeMTMCreID,
'msmt17': MSMT17,
'viper': VIPeR,
Expand Down
1 change: 1 addition & 0 deletions data_v2/datasets/image/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@
from .cuhk02 import CUHK02
from .cuhk03_detected import CUHK03_Detected
from .cuhk03_labeled import CUHK03_Labeled
from .cuhk03_splited import CUHK03_splited
from .msmt17 import MSMT17
from .mot17 import MOT17
93 changes: 93 additions & 0 deletions data_v2/datasets/image/cuhk03_splited.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
from __future__ import absolute_import
from __future__ import print_function
from __future__ import division

import sys
import os
import os.path as osp

from .. import ImageDataset
from ..utils import mkdir_if_missing, read_json, write_json


class CUHK03_splited(ImageDataset):
"""CUHK03.
Reference:
Li et al. DeepReID: Deep Filter Pairing Neural Network for Person Re-identification. CVPR 2014.
URL: `<http://www.ee.cuhk.edu.hk/~xgwang/CUHK_identification.html#!>`_
Dataset statistics:
- identities: 1360.
- images: 13164.
- cameras: 6.
- splits: 20 (classic).
"""
dataset_dir = 'CUHK03'
#dataset_url = None

def __init__(self, root='', split_id=0, cuhk03_labeled=False, cuhk03_classic_split=False, **kwargs):
self.root = osp.abspath(osp.expanduser(root))
self.dataset_dir = osp.join(self.root, self.dataset_dir)
#self.download_dataset(self.dataset_dir, self.dataset_url)

# self.data_dir = osp.join(self.dataset_dir, 'cuhk03_release')
# self.raw_mat_path = osp.join(self.data_dir, 'cuhk-03.mat')

self.imgs_detected_dir = osp.join(self.dataset_dir, 'images_detected')
self.imgs_labeled_dir = osp.join(self.dataset_dir, 'images_labeled')

self.split_classic_det_json_path = osp.join(
self.dataset_dir, 'splits_classic_detected.json')
self.split_classic_lab_json_path = osp.join(
self.dataset_dir, 'splits_classic_labeled.json')

self.split_new_det_json_path = osp.join(
self.dataset_dir, 'splits_new_detected.json')
self.split_new_lab_json_path = osp.join(
self.dataset_dir, 'splits_new_labeled.json')

self.split_new_det_mat_path = osp.join(
self.dataset_dir, 'cuhk03_new_protocol_config_detected.mat')
self.split_new_lab_mat_path = osp.join(
self.dataset_dir, 'cuhk03_new_protocol_config_labeled.mat')

required_files = [
self.dataset_dir,
# self.data_dir,
# self.raw_mat_path,
self.split_new_det_mat_path,
self.split_new_lab_mat_path
]
self.check_before_run(required_files)

# self.preprocess_split()

if cuhk03_labeled:
split_path = self.split_classic_lab_json_path if cuhk03_classic_split else self.split_new_lab_json_path
else:
split_path = self.split_classic_det_json_path if cuhk03_classic_split else self.split_new_det_json_path

splits = read_json(split_path)
assert split_id < len(splits), 'Condition split_id ({}) < len(splits) ({}) is false'.format(
split_id, len(splits))
split = splits[split_id]

train = split['train']
query = split['query']
gallery = split['gallery']
new_train_list = []
new_query_list = []
new_gallery_list = []
for item in train:
new_train_list.append(
[osp.join(self.dataset_dir, item[0][31:]), item[1], item[2]])
for item in query:
new_query_list.append(
[osp.join(self.dataset_dir, item[0][31:]), item[1], item[2]])
for item in gallery:
new_gallery_list.append(
[osp.join(self.dataset_dir, item[0][31:]), item[1], item[2]])
super(CUHK03_splited, self).__init__(new_train_list,
new_query_list, new_gallery_list, **kwargs)
6 changes: 5 additions & 1 deletion data_v2/datasets/image/dukemtmcreid.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ def process_dir(self, dir_path, relabel=False):
for img_path in img_paths:
pid, _ = map(int, pattern.search(img_path).groups())
pid_container.add(pid)
pid2label = {pid:label for label, pid in enumerate(pid_container)}
####### modified ########
pid2label = {pid:label for label, pid in enumerate(sorted(pid_container))}
# pid2label = {pid:label for label, pid in enumerate(pid_container)}

#########################

data = []
for img_path in img_paths:
Expand Down
6 changes: 5 additions & 1 deletion data_v2/datasets/image/market1501.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@ def process_dir(self, dir_path, relabel=False):
if pid == -1:
continue # junk images are just ignored
pid_container.add(pid)
pid2label = {pid:label for label, pid in enumerate(pid_container)}
####### modified #######
pid2label = {pid:label for label, pid in enumerate(sorted(pid_container))}
# pid2label = {pid:label for label, pid in enumerate(pid_container)}

########################

data = []
for img_path in img_paths:
Expand Down
6 changes: 3 additions & 3 deletions engine_v3.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def __init__(self, args, model, optimizer, scheduler, loss, loader, ckpt):

if torch.cuda.is_available():
self.ckpt.write_log('[INFO] GPU: ' + torch.cuda.get_device_name(0))
# print(torch.backends.cudnn.benchmark)

self.ckpt.write_log(
'[INFO] Starting from epoch {}'.format(self.scheduler.last_epoch + 1))
Expand Down Expand Up @@ -53,7 +54,6 @@ def train(self):

self.optimizer.zero_grad()
outputs = self.model(inputs)

loss = self.loss.compute(outputs, labels)

loss.backward()
Expand All @@ -64,6 +64,8 @@ def train(self):
batch + 1, len(self.train_loader),
self.loss.display_loss(batch)),
end='' if batch + 1 != len(self.train_loader) else '\n')
# if batch == 0:
# break

self.scheduler.step()
self.loss.end_log(len(self.train_loader))
Expand Down Expand Up @@ -162,8 +164,6 @@ def extract_feature(self, loader, args):
inputs, pid, camid = self._parse_data_for_eval(d)
input_img = inputs.to(self.device)
outputs = self.model(input_img)
# print(outputs.shape)
# if args.feat_inference == 'after':

f1 = outputs.data.cpu()
# flip
Expand Down
Loading

0 comments on commit a17dd49

Please sign in to comment.