Skip to content

Commit 7575bca

Browse files
authored
Faster R-CNN estimator (#1338)
* move rcnn forward backward task to model zoo * revert #1249 * fix * fix * docstring * fix style * add docs * faster rcnn estimator * refactor * move dataset to init * lint * merge * disable sacred config for now * logger fix * fix fit
1 parent 421f1e3 commit 7575bca

File tree

14 files changed

+566
-513
lines changed

14 files changed

+566
-513
lines changed

docs/tutorials/pose/cam_demo.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
detector.reset_class(classes=['person'], reuse_weights={'person':'person'})
5353
detector.hybridize()
5454
55-
Next for the estimator, we choose ``simple_pose_resnet18_v1b`` for it is light-weighted.
55+
Next for the estimators, we choose ``simple_pose_resnet18_v1b`` for it is light-weighted.
5656
5757
The default ``simple_pose_resnet18_v1b`` model was trained with input size 256x192.
5858
We also provide an optional ``simple_pose_resnet18_v1b`` model trained with input size 128x96.
@@ -61,8 +61,8 @@
6161
6262
.. code-block:: python
6363
64-
estimator = get_model('simple_pose_resnet18_v1b', pretrained='ccd24037', ctx=ctx)
65-
estimator.hybridize()
64+
estimators = get_model('simple_pose_resnet18_v1b', pretrained='ccd24037', ctx=ctx)
65+
estimators.hybridize()
6666
6767
With OpenCV, we can easily retrieve frames from the webcam.
6868
@@ -107,7 +107,7 @@
107107
pose_input, upscale_bbox = detector_to_simple_pose(frame, class_IDs, scores, bounding_boxs,
108108
output_shape=(128, 96), ctx=ctx)
109109
if len(upscale_bbox) > 0:
110-
predicted_heatmap = estimator(pose_input)
110+
predicted_heatmap = estimators(pose_input)
111111
pred_coords, confidence = heatmap_to_coord(predicted_heatmap, upscale_bbox)
112112
113113
img = cv_plot_keypoints(frame, pred_coords, confidence, class_IDs, bounding_boxs, scores,

gluoncv/data/sampler.py

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
# pylint: disable=line-too-long,too-many-lines,missing-docstring
22
import random
3-
from mxnet import gluon
3+
44
import numpy as np
5+
from mxnet import gluon
6+
7+
__all__ = ['SplitSampler', 'ShuffleSplitSampler', 'SplitSortedBucketSampler']
58

6-
__all__ = ['SplitSampler', 'ShuffleSplitSampler']
79

810
class SplitSampler(gluon.data.sampler.Sampler):
911
""" Split the dataset into `num_parts` parts and sample from the part with index `part_index`
@@ -17,6 +19,7 @@ class SplitSampler(gluon.data.sampler.Sampler):
1719
part_index: int
1820
The index of the part to read from
1921
"""
22+
2023
def __init__(self, length, num_parts=1, part_index=0):
2124
# Compute the length of each partition
2225
self.part_len = length // num_parts
@@ -34,6 +37,7 @@ def __iter__(self):
3437
def __len__(self):
3538
return self.part_len
3639

40+
3741
class ShuffleSplitSampler(gluon.data.sampler.Sampler):
3842
"""Split the dataset into `num_parts` parts and randomly sample from the part
3943
with index `part_index`.
@@ -48,9 +52,11 @@ class ShuffleSplitSampler(gluon.data.sampler.Sampler):
4852
part_index: int
4953
The index of the part to read from
5054
"""
55+
5156
def __init__(self, length, num_parts=1, part_index=0, seed=0):
5257
if length % num_parts != 0:
53-
print('Length ({}) must be a multiple of the number of partitions ({}).'.format(length, num_parts))
58+
print('Length ({}) must be a multiple of the number of partitions ({}).'.format(length,
59+
num_parts))
5460
self._seed = seed
5561
self._state = np.random.RandomState(seed)
5662
self._indices = list(range(length))
@@ -71,3 +77,89 @@ def __iter__(self):
7177

7278
def __len__(self):
7379
return self._end - self._start
80+
81+
82+
class SplitSortedBucketSampler(gluon.data.sampler.Sampler):
83+
r"""Batches are sampled from sorted buckets of data.
84+
First, partition data in buckets of size `batch_size * mult`.
85+
Each bucket contains `batch_size * mult` elements. The samples inside each bucket are sorted
86+
based on sort_key and then batched.
87+
Parameters
88+
----------
89+
sort_keys : list-like object
90+
The keys to sort the samples.
91+
batch_size : int
92+
Batch size of the sampler.
93+
mult : int or float, default 100
94+
The multiplier to determine the bucket size. Each bucket will have size `mult * batch_size`.
95+
num_parts: int, default 1
96+
Number of partitions which the data is split into
97+
part_index: int, default 0
98+
The index of the part to read from
99+
shuffle : bool, default False
100+
Whether to shuffle the data.
101+
Examples
102+
--------
103+
>>> lengths = [np.random.randint(1, 1000) for _ in range(1000)]
104+
>>> sampler = gluoncv.data.SplitSortedBucketSampler(lengths, 16, 1000)
105+
>>> # The sequence lengths within the batch will be sorted
106+
>>> for i, indices in enumerate(sampler):
107+
... if i == 0:
108+
... print([lengths[ele] for ele in indices])
109+
[-etc-]
110+
"""
111+
112+
def __init__(self, sort_keys, batch_size, mult=32, num_parts=1, part_index=0, shuffle=False,
113+
seed=233):
114+
assert len(sort_keys) > 0
115+
assert batch_size > 0
116+
assert mult >= 1, 'Bucket size multiplier must be greater or equal to 1'
117+
self._sort_keys = sort_keys
118+
length = len(sort_keys)
119+
self._batch_size = batch_size
120+
self._mult = mult
121+
self._shuffle = shuffle
122+
# Compute the length of each partition
123+
part_len = int(np.ceil(float(length) / num_parts))
124+
# Compute the start index for this partition
125+
self._start = part_len * part_index
126+
# Compute the end index for this partition
127+
self._end = self._start + part_len
128+
if part_index == num_parts - 1:
129+
# last part
130+
self._end = length
131+
self._start = length - part_len
132+
self._num_parts = num_parts
133+
self._seed = seed
134+
self._shuffled_ids = np.random.RandomState(seed=self._seed).permutation(range(length))
135+
136+
def __iter__(self):
137+
if self._num_parts > 1:
138+
self._shuffled_ids = np.random.RandomState(seed=self._seed).permutation(
139+
self._shuffled_ids)
140+
if self._shuffle:
141+
sample_ids = np.random.permutation(self._shuffled_ids[self._start:self._end])
142+
else:
143+
sample_ids = list(range(self._start, self._end))
144+
bucket_size = int(self._mult * self._batch_size)
145+
for bucket_begin in range(0, len(sample_ids), bucket_size):
146+
bucket_end = min(bucket_begin + bucket_size, len(sample_ids))
147+
if bucket_end - bucket_begin < self._batch_size:
148+
bucket_begin = bucket_end - self._batch_size
149+
sorted_sample_ids = sorted(sample_ids[bucket_begin:bucket_end],
150+
key=lambda i: self._sort_keys[i],
151+
reverse=random.randint(0, 1))
152+
batch_begins = list(range(0, len(sorted_sample_ids), self._batch_size))
153+
if self._shuffle:
154+
np.random.shuffle(batch_begins)
155+
for batch_begin in batch_begins:
156+
batch_end = min(batch_begin + self._batch_size, len(sorted_sample_ids))
157+
if batch_end - batch_begin < self._batch_size:
158+
yield sorted_sample_ids[batch_end - self._batch_size:batch_end]
159+
else:
160+
yield sorted_sample_ids[batch_begin:batch_end]
161+
162+
def __len__(self):
163+
length = int(np.ceil(float(self._end - self._start) / self._batch_size))
164+
assert length >= 0
165+
return length

gluoncv/loss.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ class SSDMultiBoxLoss(gluon.Block):
110110
negative_mining_ratio : float, default is 3
111111
Ratio of negative vs. positive samples.
112112
rho : float, default is 1.0
113-
Threshold for trimmed mean estimator. This is the smooth parameter for the
113+
Threshold for trimmed mean estimators. This is the smooth parameter for the
114114
L1-L2 transition.
115115
lambd : float, default is 1.0
116116
Relative weight between classification and box regression loss.

gluoncv/model_zoo/rcnn/faster_rcnn/data_parallel.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class ForwardBackwardTask(Parallelizable):
2222
R-CNN box head classification loss.
2323
rcnn_box_loss : gluon.loss
2424
R-CNN box head regression loss.
25-
mix_ratio : int
25+
mix_ratio : float
2626
Object detection mixup ratio.
2727
amp_enabled : bool
2828
Whether to enable Automatic Mixed Precision.

gluoncv/nn/sampler.py

Lines changed: 0 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,9 @@
99
"""
1010
from __future__ import absolute_import
1111

12-
import random
13-
1412
import numpy as np
1513
import mxnet as mx
1614
from mxnet import gluon, nd
17-
from mxnet.gluon.data import Sampler
1815

1916

2017
class NaiveSampler(gluon.HybridBlock):
@@ -359,125 +356,3 @@ def create_operator(self, ctx, in_shapes, in_dtypes):
359356
return QuotaSamplerOp(self.num_sample, self.pos_thresh, self.neg_thresh_high,
360357
self.neg_thresh_low, self.pos_ratio, self.neg_ratio,
361358
self.fill_negative)
362-
363-
364-
class SplitSampler(Sampler):
365-
"""Split the dataset into `num_parts` parts and randomly sample from the part
366-
with index `part_index`.
367-
368-
The data is randomly shuffled at each iteration within each partition.
369-
370-
Parameters
371-
----------
372-
length: int
373-
Number of examples in the dataset
374-
num_parts: int
375-
Number of partitions which the data is split into
376-
part_index: int
377-
The index of the part to read from
378-
"""
379-
380-
def __init__(self, length, num_parts=1, part_index=0):
381-
# Compute the length of each partition
382-
part_len = length // num_parts
383-
# Compute the start index for this partition
384-
self._start = part_len * part_index
385-
# Compute the end index for this partition
386-
self._end = self._start + part_len
387-
if part_index == num_parts - 1:
388-
self._end = length
389-
390-
def __iter__(self):
391-
# Extract examples between `start` and `end`, shuffle and return them.
392-
indices = list(range(self._start, self._end))
393-
random.shuffle(indices)
394-
return iter(indices)
395-
396-
def __len__(self):
397-
return self._end - self._start
398-
399-
400-
class SplitSortedBucketSampler(Sampler):
401-
r"""Batches are sampled from sorted buckets of data.
402-
First, partition data in buckets of size `batch_size * mult`.
403-
Each bucket contains `batch_size * mult` elements. The samples inside each bucket are sorted
404-
based on sort_key and then batched.
405-
Parameters
406-
----------
407-
sort_keys : list-like object
408-
The keys to sort the samples.
409-
batch_size : int
410-
Batch size of the sampler.
411-
mult : int or float, default 100
412-
The multiplier to determine the bucket size. Each bucket will have size `mult * batch_size`.
413-
num_parts: int, default 1
414-
Number of partitions which the data is split into
415-
part_index: int, default 0
416-
The index of the part to read from
417-
shuffle : bool, default False
418-
Whether to shuffle the data.
419-
Examples
420-
--------
421-
>>> lengths = [np.random.randint(1, 1000) for _ in range(1000)]
422-
>>> sampler = gluoncv.data.SplitSortedBucketSampler(lengths, 16, 1000)
423-
>>> # The sequence lengths within the batch will be sorted
424-
>>> for i, indices in enumerate(sampler):
425-
... if i == 0:
426-
... print([lengths[ele] for ele in indices])
427-
[-etc-]
428-
"""
429-
430-
def __init__(self, sort_keys, batch_size, mult=32, num_parts=1, part_index=0, shuffle=False,
431-
seed=233):
432-
assert len(sort_keys) > 0
433-
assert batch_size > 0
434-
assert mult >= 1, 'Bucket size multiplier must be greater or equal to 1'
435-
self._sort_keys = sort_keys
436-
length = len(sort_keys)
437-
self._batch_size = batch_size
438-
self._mult = mult
439-
self._shuffle = shuffle
440-
# Compute the length of each partition
441-
part_len = int(np.ceil(float(length) / num_parts))
442-
# Compute the start index for this partition
443-
self._start = part_len * part_index
444-
# Compute the end index for this partition
445-
self._end = self._start + part_len
446-
if part_index == num_parts - 1:
447-
# last part
448-
self._end = length
449-
self._start = length - part_len
450-
self._num_parts = num_parts
451-
self._seed = seed
452-
self._shuffled_ids = np.random.RandomState(seed=self._seed).permutation(range(length))
453-
454-
def __iter__(self):
455-
if self._num_parts > 1:
456-
self._shuffled_ids = np.random.RandomState(seed=self._seed).permutation(
457-
self._shuffled_ids)
458-
if self._shuffle:
459-
sample_ids = np.random.permutation(self._shuffled_ids[self._start:self._end])
460-
else:
461-
sample_ids = list(range(self._start, self._end))
462-
bucket_size = int(self._mult * self._batch_size)
463-
for bucket_begin in range(0, len(sample_ids), bucket_size):
464-
bucket_end = min(bucket_begin + bucket_size, len(sample_ids))
465-
if bucket_end - bucket_begin < self._batch_size:
466-
bucket_begin = bucket_end - self._batch_size
467-
sorted_sample_ids = sorted(sample_ids[bucket_begin:bucket_end],
468-
key=lambda i: self._sort_keys[i],
469-
reverse=random.randint(0, 1))
470-
batch_begins = list(range(0, len(sorted_sample_ids), self._batch_size))
471-
if self._shuffle:
472-
np.random.shuffle(batch_begins)
473-
for batch_begin in batch_begins:
474-
batch_end = min(batch_begin + self._batch_size, len(sorted_sample_ids))
475-
if batch_end - batch_begin < self._batch_size:
476-
yield sorted_sample_ids[batch_end - self._batch_size:batch_end]
477-
else:
478-
yield sorted_sample_ids[batch_begin:batch_end]
479-
480-
def __len__(self):
481-
length = int(np.ceil(float(self._end - self._start) / self._batch_size))
482-
assert length >= 0
483-
return length

gluoncv/pipelines/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
"""GluonCV pipelines"""
2-
from .estimator import *
2+
from .estimators import *

0 commit comments

Comments
 (0)