Skip to content

Commit d2a9797

Browse files
committed
UCF-101 dataset mean and proper normalization
1 parent d50b7a3 commit d2a9797

6 files changed

Lines changed: 73 additions & 33 deletions

File tree

config.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ def parse_opts():
1414
parser.add_argument('--dataset', type=str, required=True, help='Dataset string (kinetics | activitynet | ucf101 | blender)')
1515
parser.add_argument('--num_val_samples', type=int, default=1, help='Number of validation samples for each activity')
1616
parser.add_argument('--norm_value', default=255, type=int, help='Divide inputs by 255 or 1')
17+
parser.add_argument('--no_dataset_mean', action='store_true', help='Dont use the dataset mean but normalize to zero mean')
18+
parser.add_argument('--no_dataset_std', action='store_true', help='Dont use the dataset std but normalize to unity std')
1719
parser.add_argument('--num_classes', default=400, type=int, help= 'Number of classes (activitynet: 200, kinetics: 400, ucf101: 101, hmdb51: 51)')
20+
parser.set_defaults(no_dataset_std=True)
1821

1922
# Preprocessing pipeline
2023
parser.add_argument('--spatial_size', default=224, type=int, help='Height and width of inputs')
@@ -59,12 +62,13 @@ def parse_opts():
5962
parser.add_argument('--checkpoint_frequency', type=int, default=1, help='Save checkpoint after this number of epochs')
6063
parser.add_argument('--checkpoints_num_keep', type=int, default=5, help='Number of checkpoints to keep')
6164
parser.add_argument('--log_frequency', type=int, default=5, help='Logging frequency in number of steps')
65+
parser.add_argument('--log_image_frequency', type=int, default=200, help='Logging images frequency in number of steps')
6266
parser.add_argument('--no_tensorboard', action='store_true', default=False, help='Disable the use of TensorboardX')
6367

6468
# Misc
6569
parser.add_argument('--device', default='cuda:0', help='Device string cpu | cuda:0')
6670
parser.add_argument('--history_steps', default=25, type=int, help='History of running average meters')
67-
parser.add_argument('--num_workers', default=4, type=int, help='Number of threads for multi-thread loading')
71+
parser.add_argument('--num_workers', default=6, type=int, help='Number of threads for multi-thread loading')
6872
parser.add_argument('--no_eval', action='store_true', default=False, help='Disable evaluation')
6973

7074
return parser.parse_args()

datasets/ucf101.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@
1111

1212
from utils.utils import load_value_file
1313

14+
##########################################################################################
15+
##########################################################################################
1416

1517
def pil_loader(path):
1618
# open path as file to avoid ResourceWarning (https://github.com/python-pillow/Pillow/issues/835)
1719
with open(path, 'rb') as f:
1820
with Image.open(f) as img:
1921
return img.convert('RGB')
2022

21-
2223
def accimage_loader(path):
2324
try:
2425
import accimage
@@ -27,15 +28,13 @@ def accimage_loader(path):
2728
# Potentially a decoding problem, fall back to PIL.Image
2829
return pil_loader(path)
2930

30-
3131
def get_default_image_loader():
3232
from torchvision import get_image_backend
3333
if get_image_backend() == 'accimage':
3434
return accimage_loader
3535
else:
3636
return pil_loader
3737

38-
3938
def video_loader(video_dir_path, frame_indices, image_loader):
4039
video = []
4140
for i in frame_indices:
@@ -47,17 +46,14 @@ def video_loader(video_dir_path, frame_indices, image_loader):
4746

4847
return video
4948

50-
5149
def get_default_video_loader():
5250
image_loader = get_default_image_loader()
5351
return functools.partial(video_loader, image_loader=image_loader)
5452

55-
5653
def load_annotation_data(data_file_path):
5754
with open(data_file_path, 'r') as data_file:
5855
return json.load(data_file)
5956

60-
6157
def get_class_labels(data):
6258
class_labels_map = {}
6359
index = 0
@@ -66,7 +62,6 @@ def get_class_labels(data):
6662
index += 1
6763
return class_labels_map
6864

69-
7065
def get_video_names_and_annotations(data, subset):
7166
video_names = []
7267
annotations = []
@@ -80,6 +75,8 @@ def get_video_names_and_annotations(data, subset):
8075

8176
return video_names, annotations
8277

78+
##########################################################################################
79+
##########################################################################################
8380

8481
def make_dataset(root_path, annotation_path, subset, n_samples_for_each_video,
8582
sample_duration):
@@ -143,8 +140,8 @@ def make_dataset(root_path, annotation_path, subset, n_samples_for_each_video,
143140

144141
return dataset, idx_to_class
145142

146-
############################################################################
147-
############################################################################
143+
##########################################################################################
144+
##########################################################################################
148145

149146
class UCF101(data.Dataset):
150147
"""

epoch_iterators.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def train_epoch(config, model, criterion, optimizer, device,
4545
optimizer.zero_grad()
4646

4747
# Move inputs to GPU memory
48-
clips = clips.to(device)
48+
clips = clips.to(device)
4949
targets = targets.to(device)
5050
if config.model == 'i3d':
5151
targets = torch.unsqueeze(targets, -1)
@@ -97,6 +97,16 @@ def train_epoch(config, model, criterion, optimizer, device,
9797
summary_writer.add_scalar('train/learning_rate', current_learning_rate(optimizer), global_step)
9898
summary_writer.add_scalar('train/weight_decay', current_weight_decay(optimizer), global_step)
9999

100+
if summary_writer and step % config.log_image_frequency == 0:
101+
# TensorboardX video summary
102+
for example_idx in range(4):
103+
clip_for_display = clips[example_idx].clone().cpu()
104+
min_val = float(clip_for_display.min())
105+
max_val = float(clip_for_display.max())
106+
clip_for_display.clamp_(min=min_val, max=max_val)
107+
clip_for_display.add_(-min_val).div_(max_val - min_val + 1e-5)
108+
summary_writer.add_video('train_clips/{:04d}'.format(example_idx), clip_for_display.unsqueeze(0), global_step)
109+
100110
# Epoch statistics
101111
epoch_duration = float(time.time() - epoch_start_time)
102112
epoch_avg_loss = np.mean(losses)
@@ -159,6 +169,16 @@ def validation_epoch(config, model, criterion, device, data_loader, epoch, summa
159169
step, steps_in_epoch, examples_per_second,
160170
accuracies[step], losses[step]))
161171

172+
if summary_writer and step == 0:
173+
# TensorboardX video summary
174+
for example_idx in range(4):
175+
clip_for_display = clips[example_idx].clone().cpu()
176+
min_val = float(clip_for_display.min())
177+
max_val = float(clip_for_display.max())
178+
clip_for_display.clamp_(min=min_val, max=max_val)
179+
clip_for_display.add_(-min_val).div_(max_val - min_val + 1e-5)
180+
summary_writer.add_video('validation_clips/{:04d}'.format(example_idx), clip_for_display.unsqueeze(0), epoch*steps_in_epoch)
181+
162182
# Epoch statistics
163183
epoch_duration = float(time.time() - epoch_start_time)
164184
epoch_avg_loss = np.mean(losses)

factory/data_factory.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,8 @@ def get_data_loaders(config, train_transforms, validation_transforms=None):
209209
if not config.no_eval and validation_transforms:
210210

211211
dataset_validation = get_validation_set(
212-
config, train_transforms['spatial'],
213-
train_transforms['temporal'], train_transforms['target'])
212+
config, validation_transforms['spatial'],
213+
validation_transforms['temporal'], validation_transforms['target'])
214214

215215
print('Found {} validation examples'.format(len(dataset_validation)))
216216

train.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,14 @@
2121
from datetime import datetime
2222

2323
import torch.nn as nn
24-
import torch.backends.cudnn as cudnn
2524

26-
from transforms.spatial_transforms import Compose, Normalize, RandomHorizontalFlip, \
27-
RandomVerticalFlip, MultiScaleRandomCrop, ToTensor, CenterCrop
25+
from transforms.spatial_transforms import Compose, Normalize, RandomHorizontalFlip, MultiScaleRandomCrop, ToTensor, CenterCrop
2826
from transforms.temporal_transforms import TemporalRandomCrop
2927
from transforms.target_transforms import ClassLabel
3028

3129
from epoch_iterators import train_epoch, validation_epoch
3230
from utils.utils import *
31+
import utils.mean_values
3332
import factory.data_factory as data_factory
3433
import factory.model_factory as model_factory
3534
from config import parse_opts
@@ -43,6 +42,9 @@
4342
config = init_cropping_scales(config)
4443
config = set_lr_scheduling_policy(config)
4544

45+
config.image_mean = utils.mean_values.get_mean(config.norm_value, config.dataset)
46+
config.image_std = utils.mean_values.get_std(config.norm_value)
47+
4648
print_config(config)
4749
write_config(config, os.path.join(config.save_dir, 'config.json'))
4850

@@ -75,17 +77,39 @@
7577
####################################################################
7678
# Setup of data transformations
7779

80+
if config.no_dataset_mean and config.no_dataset_std:
81+
# Just zero-center and scale to unit std
82+
print('Data normalization: no dataset mean, no dataset std')
83+
norm_method = Normalize([0, 0, 0], [1, 1, 1])
84+
elif not config.no_dataset_mean and config.no_dataset_std:
85+
# Subtract dataset mean and scale to unit std
86+
print('Data normalization: use dataset mean, no dataset std')
87+
norm_method = Normalize(config.image_mean, [1, 1, 1])
88+
else:
89+
# Subtract dataset mean and scale to dataset std
90+
print('Data normalization: use dataset mean, use dataset std')
91+
norm_method = Normalize(config.image_mean, config.image_std)
92+
7893
train_transforms = {
7994
'spatial': Compose([MultiScaleRandomCrop(config.scales, config.spatial_size),
80-
RandomHorizontalFlip(), RandomVerticalFlip(),
81-
ToTensor(config.norm_value), Normalize([0, 0, 0], [1, 1, 1])]),
95+
RandomHorizontalFlip(),
96+
ToTensor(config.norm_value),
97+
norm_method]),
8298
'temporal': TemporalRandomCrop(config.sample_duration),
8399
'target': ClassLabel()
84100
}
85101

102+
# print('WARNING: setting train transforms for dataset statistics')
103+
# train_transforms = {
104+
# 'spatial': Compose([ToTensor(1.0)]),
105+
# 'temporal': TemporalRandomCrop(64),
106+
# 'target': ClassLabel()
107+
# }
108+
86109
validation_transforms = {
87-
'spatial': Compose([CenterCrop(config.spatial_size), ToTensor(config.norm_value),
88-
Normalize([0, 0, 0], [1, 1, 1])]),
110+
'spatial': Compose([CenterCrop(config.spatial_size),
111+
ToTensor(config.norm_value),
112+
norm_method]),
89113
'temporal': TemporalRandomCrop(config.sample_duration),
90114
'target': ClassLabel()
91115
}

utils/mean_values.py

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,18 @@
11
# Source: https://github.com/kenshohara/3D-ResNets-PyTorch/blob/master/mean.py
22

33
def get_mean(norm_value=255, dataset='activitynet'):
4-
assert dataset in ['activitynet', 'kinetics']
4+
5+
# Below values are in RGB order
6+
assert dataset in ['activitynet', 'kinetics', 'ucf101']
57

68
if dataset == 'activitynet':
7-
return [
8-
114.7748 / norm_value, 107.7354 / norm_value, 99.4750 / norm_value
9-
]
9+
return [114.7748/norm_value, 107.7354/norm_value, 99.4750/norm_value]
1010
elif dataset == 'kinetics':
1111
# Kinetics (10 videos for each class)
12-
return [
13-
110.63666788 / norm_value, 103.16065604 / norm_value,
14-
96.29023126 / norm_value
15-
]
16-
12+
return [110.63666788/norm_value, 103.16065604/norm_value, 96.29023126/norm_value]
13+
elif dataset == 'ucf101':
14+
return [101.00131/norm_value, 97.3644226/norm_value, 89.42114168/norm_value]
1715

1816
def get_std(norm_value=255):
1917
# Kinetics (10 videos for each class)
20-
return [
21-
38.7568578 / norm_value, 37.88248729 / norm_value,
22-
40.02898126 / norm_value
23-
]
18+
return [38.7568578/norm_value, 37.88248729/norm_value, 40.02898126/norm_value]

0 commit comments

Comments
 (0)