| @@ -26,7 +26,7 @@ def _clear_plasma_store(): | |||||
| # `_PlasmaStoreManager.__del__` will not be called automaticly in subprocess, | # `_PlasmaStoreManager.__del__` will not be called automaticly in subprocess, | ||||
| # so this function should be called explicitly | # so this function should be called explicitly | ||||
| global MGE_PLASMA_STORE_MANAGER | global MGE_PLASMA_STORE_MANAGER | ||||
| if MGE_PLASMA_STORE_MANAGER is not None: | |||||
| if MGE_PLASMA_STORE_MANAGER is not None and MGE_PLASMA_STORE_MANAGER.refcount == 0: | |||||
| del MGE_PLASMA_STORE_MANAGER | del MGE_PLASMA_STORE_MANAGER | ||||
| MGE_PLASMA_STORE_MANAGER = None | MGE_PLASMA_STORE_MANAGER = None | ||||
| @@ -50,6 +50,7 @@ class _PlasmaStoreManager: | |||||
| stderr=None if debug_flag else subprocess.DEVNULL, | stderr=None if debug_flag else subprocess.DEVNULL, | ||||
| ) | ) | ||||
| self.__initialized = True | self.__initialized = True | ||||
| self.refcount = 1 | |||||
| def __del__(self): | def __del__(self): | ||||
| if self.__initialized and self.plasma_store.returncode is None: | if self.__initialized and self.plasma_store.returncode is None: | ||||
| @@ -83,6 +84,8 @@ class PlasmaShmQueue: | |||||
| "Exception happened in starting plasma_store: {}\n" | "Exception happened in starting plasma_store: {}\n" | ||||
| "Tips: {}".format(str(e), err_info) | "Tips: {}".format(str(e), err_info) | ||||
| ) | ) | ||||
| else: | |||||
| MGE_PLASMA_STORE_MANAGER.refcount += 1 | |||||
| self.socket_name = MGE_PLASMA_STORE_MANAGER.socket_name | self.socket_name = MGE_PLASMA_STORE_MANAGER.socket_name | ||||
| @@ -133,6 +136,8 @@ class PlasmaShmQueue: | |||||
| def close(self): | def close(self): | ||||
| self.queue.close() | self.queue.close() | ||||
| self.disconnect_client() | self.disconnect_client() | ||||
| global MGE_PLASMA_STORE_MANAGER | |||||
| MGE_PLASMA_STORE_MANAGER.refcount -= 1 | |||||
| _clear_plasma_store() | _clear_plasma_store() | ||||
| def cancel_join_thread(self): | def cancel_join_thread(self): | ||||
| @@ -118,7 +118,7 @@ class COCO(VisionDataset): | |||||
| self.ids = ids | self.ids = ids | ||||
| self.json_category_id_to_contiguous_id = { | self.json_category_id_to_contiguous_id = { | ||||
| v: i + 1 for i, v in enumerate(self.cats.keys()) | |||||
| v: i + 1 for i, v in enumerate(sorted(self.cats.keys())) | |||||
| } | } | ||||
| self.contiguous_category_id_to_json_id = { | self.contiguous_category_id_to_json_id = { | ||||
| @@ -81,7 +81,7 @@ class Objects365(VisionDataset): | |||||
| self.ids = ids | self.ids = ids | ||||
| self.json_category_id_to_contiguous_id = { | self.json_category_id_to_contiguous_id = { | ||||
| v: i + 1 for i, v in enumerate(self.cats.keys()) | |||||
| v: i + 1 for i, v in enumerate(sorted(self.cats.keys())) | |||||
| } | } | ||||
| self.contiguous_category_id_to_json_id = { | self.contiguous_category_id_to_json_id = { | ||||
| @@ -75,6 +75,8 @@ class PascalVOC(VisionDataset): | |||||
| else: | else: | ||||
| raise NotImplementedError | raise NotImplementedError | ||||
| self.img_infos = dict() | |||||
| def __getitem__(self, index): | def __getitem__(self, index): | ||||
| target = [] | target = [] | ||||
| for k in self.order: | for k in self.order: | ||||
| @@ -107,9 +109,8 @@ class PascalVOC(VisionDataset): | |||||
| mask = mask[:, :, np.newaxis] | mask = mask[:, :, np.newaxis] | ||||
| target.append(mask) | target.append(mask) | ||||
| elif k == "info": | elif k == "info": | ||||
| if image is None: | |||||
| image = cv2.imread(self.images[index], cv2.IMREAD_COLOR) | |||||
| info = [image.shape[0], image.shape[1], self.file_names[index]] | |||||
| info = self.get_img_info(index, image) | |||||
| info = [info["height"], info["width"], info["file_name"]] | |||||
| target.append(info) | target.append(info) | ||||
| else: | else: | ||||
| raise NotImplementedError | raise NotImplementedError | ||||
| @@ -119,6 +120,17 @@ class PascalVOC(VisionDataset): | |||||
| def __len__(self): | def __len__(self): | ||||
| return len(self.images) | return len(self.images) | ||||
| def get_img_info(self, index, image=None): | |||||
| if index not in self.img_infos: | |||||
| if image is None: | |||||
| image = cv2.imread(self.images[index], cv2.IMREAD_COLOR) | |||||
| self.img_infos[index] = dict( | |||||
| height=image.shape[0], | |||||
| width=image.shape[1], | |||||
| file_name=self.file_names[index], | |||||
| ) | |||||
| return self.img_infos[index] | |||||
| def _trans_mask(self, mask): | def _trans_mask(self, mask): | ||||
| label = np.ones(mask.shape[:2]) * 255 | label = np.ones(mask.shape[:2]) * 255 | ||||
| for i in range(len(self.class_colors)): | for i in range(len(self.class_colors)): | ||||
| @@ -171,25 +183,3 @@ class PascalVOC(VisionDataset): | |||||
| "train", | "train", | ||||
| "tvmonitor", | "tvmonitor", | ||||
| ) | ) | ||||
| class_colors = [ | |||||
| [0, 0, 128], | |||||
| [0, 128, 0], | |||||
| [0, 128, 128], | |||||
| [128, 0, 0], | |||||
| [128, 0, 128], | |||||
| [128, 128, 0], | |||||
| [128, 128, 128], | |||||
| [0, 0, 64], | |||||
| [0, 0, 192], | |||||
| [0, 128, 64], | |||||
| [0, 128, 192], | |||||
| [128, 0, 64], | |||||
| [128, 0, 192], | |||||
| [128, 128, 64], | |||||
| [128, 128, 192], | |||||
| [0, 64, 0], | |||||
| [0, 64, 128], | |||||
| [0, 192, 0], | |||||
| [0, 192, 128], | |||||
| [128, 64, 0], | |||||
| ] | |||||
| @@ -52,7 +52,7 @@ class QATModule(Module): | |||||
| self.weight_fake_quant = safe_call(qconfig.weight_fake_quant) | self.weight_fake_quant = safe_call(qconfig.weight_fake_quant) | ||||
| def _enable_exec(self, with_module, func, enable): | def _enable_exec(self, with_module, func, enable): | ||||
| if not with_module: | |||||
| if not with_module or not func: | |||||
| return | return | ||||
| if enable: | if enable: | ||||
| func.enable() | func.enable() | ||||
| @@ -26,40 +26,40 @@ class Sequential(Module): | |||||
| import megengine as mge | import megengine as mge | ||||
| import megengine.module as M | import megengine.module as M | ||||
| import megengine.functional as F | import megengine.functional as F | ||||
| from collections import OrderedDict | |||||
| batch_size = 64 | batch_size = 64 | ||||
| data = mge.tensor(np.zeros((batch_size, 1, 28, 28)), dtype=np.float32) | data = mge.tensor(np.zeros((batch_size, 1, 28, 28)), dtype=np.float32) | ||||
| label = mge.tensor(np.zeros(batch_size,), dtype=np.int32) | label = mge.tensor(np.zeros(batch_size,), dtype=np.int32) | ||||
| data = data.reshape(batch_size, -1) | data = data.reshape(batch_size, -1) | ||||
| net = M.Sequential( | |||||
| net0 = M.Sequential( | |||||
| M.Linear(28 * 28, 320), | M.Linear(28 * 28, 320), | ||||
| M.Linear(320, 500), | |||||
| M.Linear(500, 320), | |||||
| M.Linear(320, 10) | M.Linear(320, 10) | ||||
| ) | ) | ||||
| pred = net(data) | |||||
| pred0 = net0(data) | |||||
| loss = F.cross_entropy_with_softmax(pred, label) | |||||
| modules = OrderedDict() | |||||
| modules["fc0"] = nn.Linear(28 * 28, 320) | |||||
| modules["fc1"] = nn.Linear(320, 10) | |||||
| net1 = nn.Sequential(modules) | |||||
| pred1 = net1(data) | |||||
| """ | """ | ||||
| def __init__(self, *args): | def __init__(self, *args): | ||||
| super().__init__() | super().__init__() | ||||
| self.layer_keys = [] | self.layer_keys = [] | ||||
| self.layer_values = [] | |||||
| if len(args) == 1 and isinstance(args[0], OrderedDict): | if len(args) == 1 and isinstance(args[0], OrderedDict): | ||||
| for key, module in args[0].items(): | for key, module in args[0].items(): | ||||
| # self.add_module(key, module) | # self.add_module(key, module) | ||||
| setattr(self, key, module) | setattr(self, key, module) | ||||
| self.layer_keys.append(key) | self.layer_keys.append(key) | ||||
| self.layer_values.append(module) | |||||
| else: | else: | ||||
| for idx, module in enumerate(args): | for idx, module in enumerate(args): | ||||
| # self.add_module(str(idx), module) | # self.add_module(str(idx), module) | ||||
| setattr(self, str(idx), module) | setattr(self, str(idx), module) | ||||
| self.layer_keys.append(str(idx)) | self.layer_keys.append(str(idx)) | ||||
| self.layer_values.append(module) | |||||
| def __getitem__(self, idx): | def __getitem__(self, idx): | ||||
| if isinstance(idx, slice): | if isinstance(idx, slice): | ||||
| @@ -67,11 +67,10 @@ class Sequential(Module): | |||||
| OrderedDict(zip(self.layer_keys[idx], self.layer_values[idx])) | OrderedDict(zip(self.layer_keys[idx], self.layer_values[idx])) | ||||
| ) | ) | ||||
| else: | else: | ||||
| return self.layer_values[idx] | |||||
| return getattr(self, self.layer_keys[idx]) | |||||
| def __setitem__(self, idx, module): | def __setitem__(self, idx, module): | ||||
| key = self.layer_keys[idx] | key = self.layer_keys[idx] | ||||
| self.layer_values[idx] = module | |||||
| return setattr(self, key, module) | return setattr(self, key, module) | ||||
| def __delitem__(self, idx): | def __delitem__(self, idx): | ||||
| @@ -79,11 +78,9 @@ class Sequential(Module): | |||||
| for key in self.layer_keys[idx]: | for key in self.layer_keys[idx]: | ||||
| delattr(self, key) | delattr(self, key) | ||||
| del self.layer_keys[idx] | del self.layer_keys[idx] | ||||
| del self.layer_values[idx] | |||||
| else: | else: | ||||
| delattr(self, self.layer_keys[idx]) | delattr(self, self.layer_keys[idx]) | ||||
| del self.layer_keys[idx] | del self.layer_keys[idx] | ||||
| del self.layer_values[idx] | |||||
| def __len__(self): | def __len__(self): | ||||
| return len(self.layer_keys) | return len(self.layer_keys) | ||||
| @@ -91,6 +88,10 @@ class Sequential(Module): | |||||
| def __iter__(self): | def __iter__(self): | ||||
| return iter(self.layer_values) | return iter(self.layer_values) | ||||
| @property | |||||
| def layer_values(self): | |||||
| return [getattr(self, key) for key in self.layer_keys] | |||||
| def forward(self, inp): | def forward(self, inp): | ||||
| for layer in self.layer_values: | for layer in self.layer_values: | ||||
| inp = layer(inp) | inp = layer(inp) | ||||
| @@ -0,0 +1,183 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| # MegEngine is Licensed under the Apache License, Version 2.0 (the "License") | |||||
| # | |||||
| # Copyright (c) 2014-2020 Megvii Inc. All rights reserved. | |||||
| # | |||||
| # Unless required by applicable law or agreed to in writing, | |||||
| # software distributed under the License is distributed on an | |||||
| # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| import os | |||||
| import time | |||||
| import numpy as np | |||||
| import pytest | |||||
| from megengine.data.collator import Collator | |||||
| from megengine.data.dataloader import DataLoader | |||||
| from megengine.data.dataset import ArrayDataset | |||||
| from megengine.data.sampler import RandomSampler, SequentialSampler | |||||
| from megengine.data.transform import PseudoTransform, Transform | |||||
| def init_dataset(): | |||||
| sample_num = 100 | |||||
| rand_data = np.random.randint(0, 255, size=(sample_num, 1, 32, 32), dtype=np.uint8) | |||||
| label = np.random.randint(0, 10, size=(sample_num,), dtype=int) | |||||
| dataset = ArrayDataset(rand_data, label) | |||||
| return dataset | |||||
| def test_dataloader_init(): | |||||
| dataset = init_dataset() | |||||
| with pytest.raises(ValueError): | |||||
| dataloader = DataLoader(dataset, num_workers=2, divide=True) | |||||
| with pytest.raises(ValueError): | |||||
| dataloader = DataLoader(dataset, num_workers=-1) | |||||
| with pytest.raises(ValueError): | |||||
| dataloader = DataLoader(dataset, timeout=-1) | |||||
| with pytest.raises(ValueError): | |||||
| dataloader = DataLoader(dataset, num_workers=0, divide=True) | |||||
| dataloader = DataLoader(dataset) | |||||
| assert isinstance(dataloader.sampler, SequentialSampler) | |||||
| assert isinstance(dataloader.transform, PseudoTransform) | |||||
| assert isinstance(dataloader.collator, Collator) | |||||
| dataloader = DataLoader( | |||||
| dataset, sampler=RandomSampler(dataset, batch_size=6, drop_last=False) | |||||
| ) | |||||
| assert len(dataloader) == 17 | |||||
| dataloader = DataLoader( | |||||
| dataset, sampler=RandomSampler(dataset, batch_size=6, drop_last=True) | |||||
| ) | |||||
| assert len(dataloader) == 16 | |||||
| def test_dataloader_serial(): | |||||
| dataset = init_dataset() | |||||
| dataloader = DataLoader( | |||||
| dataset, sampler=RandomSampler(dataset, batch_size=4, drop_last=False) | |||||
| ) | |||||
| for (data, label) in dataloader: | |||||
| assert data.shape == (4, 1, 32, 32) | |||||
| assert label.shape == (4,) | |||||
| def test_dataloader_parallel(): | |||||
| # set max shared memory to 100M | |||||
| os.environ["MGE_PLASMA_MEMORY"] = "100000000" | |||||
| dataset = init_dataset() | |||||
| dataloader = DataLoader( | |||||
| dataset, | |||||
| sampler=RandomSampler(dataset, batch_size=4, drop_last=False), | |||||
| num_workers=2, | |||||
| divide=False, | |||||
| ) | |||||
| for (data, label) in dataloader: | |||||
| assert data.shape == (4, 1, 32, 32) | |||||
| assert label.shape == (4,) | |||||
| dataloader = DataLoader( | |||||
| dataset, | |||||
| sampler=RandomSampler(dataset, batch_size=4, drop_last=False), | |||||
| num_workers=2, | |||||
| divide=True, | |||||
| ) | |||||
| for (data, label) in dataloader: | |||||
| assert data.shape == (4, 1, 32, 32) | |||||
| assert label.shape == (4,) | |||||
| def test_dataloader_parallel_timeout(): | |||||
| dataset = init_dataset() | |||||
| class TimeoutTransform(Transform): | |||||
| def __init__(self): | |||||
| pass | |||||
| def apply(self, input): | |||||
| time.sleep(10) | |||||
| return input | |||||
| dataloader = DataLoader( | |||||
| dataset, | |||||
| sampler=RandomSampler(dataset, batch_size=4, drop_last=False), | |||||
| transform=TimeoutTransform(), | |||||
| num_workers=2, | |||||
| timeout=2, | |||||
| ) | |||||
| with pytest.raises(RuntimeError, match=r".*timeout.*"): | |||||
| data_iter = iter(dataloader) | |||||
| batch_data = next(data_iter) | |||||
| def test_dataloader_parallel_worker_exception(): | |||||
| dataset = init_dataset() | |||||
| class FakeErrorTransform(Transform): | |||||
| def __init__(self): | |||||
| pass | |||||
| def apply(self, input): | |||||
| y = x + 1 | |||||
| return input | |||||
| dataloader = DataLoader( | |||||
| dataset, | |||||
| sampler=RandomSampler(dataset, batch_size=4, drop_last=False), | |||||
| transform=FakeErrorTransform(), | |||||
| num_workers=2, | |||||
| ) | |||||
| with pytest.raises(RuntimeError, match=r"worker.*died"): | |||||
| data_iter = iter(dataloader) | |||||
| batch_data = next(data_iter) | |||||
| def _multi_instances_parallel_dataloader_worker(): | |||||
| dataset = init_dataset() | |||||
| for divide_flag in [True, False]: | |||||
| train_dataloader = DataLoader( | |||||
| dataset, | |||||
| sampler=RandomSampler(dataset, batch_size=4, drop_last=False), | |||||
| num_workers=2, | |||||
| divide=divide_flag, | |||||
| ) | |||||
| val_dataloader = DataLoader( | |||||
| dataset, | |||||
| sampler=RandomSampler(dataset, batch_size=10, drop_last=False), | |||||
| num_workers=2, | |||||
| divide=divide_flag, | |||||
| ) | |||||
| for idx, (data, label) in enumerate(train_dataloader): | |||||
| assert data.shape == (4, 1, 32, 32) | |||||
| assert label.shape == (4,) | |||||
| if idx % 5 == 0: | |||||
| for val_data, val_label in val_dataloader: | |||||
| assert val_data.shape == (10, 1, 32, 32) | |||||
| assert val_label.shape == (10,) | |||||
| def test_dataloader_parallel_multi_instances(): | |||||
| # set max shared memory to 100M | |||||
| os.environ["MGE_PLASMA_MEMORY"] = "100000000" | |||||
| _multi_instances_parallel_dataloader_worker() | |||||
| def test_dataloader_parallel_multi_instances_multiprocessing(): | |||||
| # set max shared memory to 100M | |||||
| os.environ["MGE_PLASMA_MEMORY"] = "100000000" | |||||
| import multiprocessing as mp | |||||
| # mp.set_start_method("spawn") | |||||
| processes = [] | |||||
| for i in range(4): | |||||
| p = mp.Process(target=_multi_instances_parallel_dataloader_worker) | |||||
| p.start() | |||||
| processes.append(p) | |||||
| for p in processes: | |||||
| p.join() | |||||
| @@ -460,9 +460,9 @@ def test_sequential_named_children(): | |||||
| modules["name2"] = Linear(5, 1) | modules["name2"] = Linear(5, 1) | ||||
| m = Sequential(modules) | m = Sequential(modules) | ||||
| l = list(m.named_children()) | l = list(m.named_children()) | ||||
| assert l[0][0] == "layer_values.0" | |||||
| assert l[1][0] == "layer_values.1" | |||||
| assert l[2][0] == "layer_values.2" | |||||
| assert l[0][0] == "name0" | |||||
| assert l[1][0] == "name1" | |||||
| assert l[2][0] == "name2" | |||||
| def test_state_dict(): | def test_state_dict(): | ||||