prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  'ConcatDataset' is not available in MMPOSE
  My config is something like this. I want to use the 'ConcatDataset' just like it in the MMDETECTION and MMSEGMENT. However, the 'ConcatDataset' is not available in MMPOSE
  ```
  # myconfig.py
  data = dict(
      train=dict(
           type = 'ConcatDataset',
           datasets = [dict(
               type='BottomUpCocoDataset',
               ann_file='data/keypoints/2021-11-02-bwrat_side6-kp_val.json',
               img_prefix='data/keypoints/2021-11-02-bwrat_side6-kp/',
               data_cfg=data_cfg,
               pipeline=test_pipeline,
              dataset_info=dataset_info),
           dict(
               type='BottomUpCocoDataset',
               ann_file='data/keypoints/2021-11-24-wrat_side6-kp_val.json',
               img_prefix='data/keypoints/2021-11-24-wrat_side6-kp/',
               data_cfg=data_cfg,
               pipeline=val_pipeline,
               dataset_info=dataset_info)
           ])
  )
  ```
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `mmpose/datasets/builder.py`
  Content:
  ```
  1 # Copyright (c) OpenMMLab. All rights reserved.
  2 import platform
  3 import random
  4 from functools import partial
  5 
  6 import numpy as np
  7 from mmcv.parallel import collate
  8 from mmcv.runner import get_dist_info
  9 from mmcv.utils import Registry, build_from_cfg
  10 from mmcv.utils.parrots_wrapper import _get_dataloader
  11 
  12 from .samplers import DistributedSampler
  13 
  14 if platform.system() != 'Windows':
  15     # https://github.com/pytorch/pytorch/issues/973
  16     import resource
  17     rlimit = resource.getrlimit(resource.RLIMIT_NOFILE)
  18     base_soft_limit = rlimit[0]
  19     hard_limit = rlimit[1]
  20     soft_limit = min(max(4096, base_soft_limit), hard_limit)
  21     resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit))
  22 
  23 DATASETS = Registry('dataset')
  24 PIPELINES = Registry('pipeline')
  25 
  26 
  27 def build_dataset(cfg, default_args=None):
  28     """Build a dataset from config dict.
  29 
  30     Args:
  31         cfg (dict): Config dict. It should at least contain the key "type".
  32         default_args (dict, optional): Default initialization arguments.
  33             Default: None.
  34 
  35     Returns:
  36         Dataset: The constructed dataset.
  37     """
  38     from .dataset_wrappers import RepeatDataset
  39 
  40     if cfg['type'] == 'RepeatDataset':
  41         dataset = RepeatDataset(
  42             build_dataset(cfg['dataset'], default_args), cfg['times'])
  43     else:
  44         dataset = build_from_cfg(cfg, DATASETS, default_args)
  45     return dataset
  46 
  47 
  48 def build_dataloader(dataset,
  49                      samples_per_gpu,
  50                      workers_per_gpu,
  51                      num_gpus=1,
  52                      dist=True,
  53                      shuffle=True,
  54                      seed=None,
  55                      drop_last=True,
  56                      pin_memory=True,
  57                      **kwargs):
  58     """Build PyTorch DataLoader.
  59 
  60     In distributed training, each GPU/process has a dataloader.
  61     In non-distributed training, there is only one dataloader for all GPUs.
  62 
  63     Args:
  64         dataset (Dataset): A PyTorch dataset.
  65         samples_per_gpu (int): Number of training samples on each GPU, i.e.,
  66             batch size of each GPU.
  67         workers_per_gpu (int): How many subprocesses to use for data loading
  68             for each GPU.
  69         num_gpus (int): Number of GPUs. Only used in non-distributed training.
  70         dist (bool): Distributed training/test or not. Default: True.
  71         shuffle (bool): Whether to shuffle the data at every epoch.
  72             Default: True.
  73         drop_last (bool): Whether to drop the last incomplete batch in epoch.
  74             Default: True
  75         pin_memory (bool): Whether to use pin_memory in DataLoader.
  76             Default: True
  77         kwargs: any keyword argument to be used to initialize DataLoader
  78 
  79     Returns:
  80         DataLoader: A PyTorch dataloader.
  81     """
  82     rank, world_size = get_dist_info()
  83     if dist:
  84         sampler = DistributedSampler(
  85             dataset, world_size, rank, shuffle=shuffle, seed=seed)
  86         shuffle = False
  87         batch_size = samples_per_gpu
  88         num_workers = workers_per_gpu
  89     else:
  90         sampler = None
  91         batch_size = num_gpus * samples_per_gpu
  92         num_workers = num_gpus * workers_per_gpu
  93 
  94     init_fn = partial(
  95         worker_init_fn, num_workers=num_workers, rank=rank,
  96         seed=seed) if seed is not None else None
  97 
  98     _, DataLoader = _get_dataloader()
  99     data_loader = DataLoader(
  100         dataset,
  101         batch_size=batch_size,
  102         sampler=sampler,
  103         num_workers=num_workers,
  104         collate_fn=partial(collate, samples_per_gpu=samples_per_gpu),
  105         pin_memory=pin_memory,
  106         shuffle=shuffle,
  107         worker_init_fn=init_fn,
  108         drop_last=drop_last,
  109         **kwargs)
  110 
  111     return data_loader
  112 
  113 
  114 def worker_init_fn(worker_id, num_workers, rank, seed):
  115     """Init the random seed for various workers."""
  116     # The seed of each worker equals to
  117     # num_worker * rank + worker_id + user_seed
  118     worker_seed = num_workers * rank + worker_id + seed
  119     np.random.seed(worker_seed)
  120     random.seed(worker_seed)
  121
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/mmpose/datasets/builder.py b/mmpose/datasets/builder.py
  --- a/mmpose/datasets/builder.py
  +++ b/mmpose/datasets/builder.py
  @@ -1,4 +1,5 @@
   # Copyright (c) OpenMMLab. All rights reserved.
  +import copy
   import platform
   import random
   from functools import partial
  @@ -6,8 +7,9 @@
   import numpy as np
   from mmcv.parallel import collate
   from mmcv.runner import get_dist_info
  -from mmcv.utils import Registry, build_from_cfg
  +from mmcv.utils import Registry, build_from_cfg, is_seq_of
   from mmcv.utils.parrots_wrapper import _get_dataloader
  +from torch.utils.data.dataset import ConcatDataset
   
   from .samplers import DistributedSampler
   
  @@ -24,6 +26,39 @@
   PIPELINES = Registry('pipeline')
   
   
  +def _concat_dataset(cfg, default_args=None):
  +    types = cfg['type']
  +    ann_files = cfg['ann_file']
  +    img_prefixes = cfg.get('img_prefix', None)
  +    dataset_infos = cfg.get('dataset_info', None)
  +
  +    num_joints = cfg['data_cfg'].get('num_joints', None)
  +    dataset_channel = cfg['data_cfg'].get('dataset_channel', None)
  +
  +    datasets = []
  +    num_dset = len(ann_files)
  +    for i in range(num_dset):
  +        cfg_copy = copy.deepcopy(cfg)
  +        cfg_copy['ann_file'] = ann_files[i]
  +
  +        if isinstance(types, (list, tuple)):
  +            cfg_copy['type'] = types[i]
  +        if isinstance(img_prefixes, (list, tuple)):
  +            cfg_copy['img_prefix'] = img_prefixes[i]
  +        if isinstance(dataset_infos, (list, tuple)):
  +            cfg_copy['dataset_info'] = dataset_infos[i]
  +
  +        if isinstance(num_joints, (list, tuple)):
  +            cfg_copy['data_cfg']['num_joints'] = num_joints[i]
  +
  +        if is_seq_of(dataset_channel, list):
  +            cfg_copy['data_cfg']['dataset_channel'] = dataset_channel[i]
  +
  +        datasets.append(build_dataset(cfg_copy, default_args))
  +
  +    return ConcatDataset(datasets)
  +
  +
   def build_dataset(cfg, default_args=None):
       """Build a dataset from config dict.
   
  @@ -37,9 +72,16 @@
       """
       from .dataset_wrappers import RepeatDataset
   
  -    if cfg['type'] == 'RepeatDataset':
  +    if isinstance(cfg, (list, tuple)):
  +        dataset = ConcatDataset([build_dataset(c, default_args) for c in cfg])
  +    elif cfg['type'] == 'ConcatDataset':
  +        dataset = ConcatDataset(
  +            [build_dataset(c, default_args) for c in cfg['datasets']])
  +    elif cfg['type'] == 'RepeatDataset':
           dataset = RepeatDataset(
               build_dataset(cfg['dataset'], default_args), cfg['times'])
  +    elif isinstance(cfg.get('ann_file'), (list, tuple)):
  +        dataset = _concat_dataset(cfg, default_args)
       else:
           dataset = build_from_cfg(cfg, DATASETS, default_args)
       return dataset
  
verification_info: {"golden_diff": "diff --git a/mmpose/datasets/builder.py b/mmpose/datasets/builder.py\n--- a/mmpose/datasets/builder.py\n+++ b/mmpose/datasets/builder.py\n@@ -1,4 +1,5 @@\n # Copyright (c) OpenMMLab. All rights reserved.\n+import copy\n import platform\n import random\n from functools import partial\n@@ -6,8 +7,9 @@\n import numpy as np\n from mmcv.parallel import collate\n from mmcv.runner import get_dist_info\n-from mmcv.utils import Registry, build_from_cfg\n+from mmcv.utils import Registry, build_from_cfg, is_seq_of\n from mmcv.utils.parrots_wrapper import _get_dataloader\n+from torch.utils.data.dataset import ConcatDataset\n \n from .samplers import DistributedSampler\n \n@@ -24,6 +26,39 @@\n PIPELINES = Registry('pipeline')\n \n \n+def _concat_dataset(cfg, default_args=None):\n+    types = cfg['type']\n+    ann_files = cfg['ann_file']\n+    img_prefixes = cfg.get('img_prefix', None)\n+    dataset_infos = cfg.get('dataset_info', None)\n+\n+    num_joints = cfg['data_cfg'].get('num_joints', None)\n+    dataset_channel = cfg['data_cfg'].get('dataset_channel', None)\n+\n+    datasets = []\n+    num_dset = len(ann_files)\n+    for i in range(num_dset):\n+        cfg_copy = copy.deepcopy(cfg)\n+        cfg_copy['ann_file'] = ann_files[i]\n+\n+        if isinstance(types, (list, tuple)):\n+            cfg_copy['type'] = types[i]\n+        if isinstance(img_prefixes, (list, tuple)):\n+            cfg_copy['img_prefix'] = img_prefixes[i]\n+        if isinstance(dataset_infos, (list, tuple)):\n+            cfg_copy['dataset_info'] = dataset_infos[i]\n+\n+        if isinstance(num_joints, (list, tuple)):\n+            cfg_copy['data_cfg']['num_joints'] = num_joints[i]\n+\n+        if is_seq_of(dataset_channel, list):\n+            cfg_copy['data_cfg']['dataset_channel'] = dataset_channel[i]\n+\n+        datasets.append(build_dataset(cfg_copy, default_args))\n+\n+    return ConcatDataset(datasets)\n+\n+\n def build_dataset(cfg, default_args=None):\n     \"\"\"Build a dataset from config dict.\n \n@@ -37,9 +72,16 @@\n     \"\"\"\n     from .dataset_wrappers import RepeatDataset\n \n-    if cfg['type'] == 'RepeatDataset':\n+    if isinstance(cfg, (list, tuple)):\n+        dataset = ConcatDataset([build_dataset(c, default_args) for c in cfg])\n+    elif cfg['type'] == 'ConcatDataset':\n+        dataset = ConcatDataset(\n+            [build_dataset(c, default_args) for c in cfg['datasets']])\n+    elif cfg['type'] == 'RepeatDataset':\n         dataset = RepeatDataset(\n             build_dataset(cfg['dataset'], default_args), cfg['times'])\n+    elif isinstance(cfg.get('ann_file'), (list, tuple)):\n+        dataset = _concat_dataset(cfg, default_args)\n     else:\n         dataset = build_from_cfg(cfg, DATASETS, default_args)\n     return dataset\n", "issue": "'ConcatDataset' is not available in MMPOSE\nMy config is something like this. I want to use the 'ConcatDataset' just like it in the MMDETECTION and MMSEGMENT. However, the 'ConcatDataset' is not available in MMPOSE\r\n```\r\n# myconfig.py\r\ndata = dict(\r\n    train=dict(\r\n         type = 'ConcatDataset',\r\n         datasets = [dict(\r\n             type='BottomUpCocoDataset',\r\n             ann_file='data/keypoints/2021-11-02-bwrat_side6-kp_val.json',\r\n             img_prefix='data/keypoints/2021-11-02-bwrat_side6-kp/',\r\n             data_cfg=data_cfg,\r\n             pipeline=test_pipeline,\r\n            dataset_info=dataset_info),\r\n         dict(\r\n             type='BottomUpCocoDataset',\r\n             ann_file='data/keypoints/2021-11-24-wrat_side6-kp_val.json',\r\n             img_prefix='data/keypoints/2021-11-24-wrat_side6-kp/',\r\n             data_cfg=data_cfg,\r\n             pipeline=val_pipeline,\r\n             dataset_info=dataset_info)\r\n         ])\r\n)\r\n```\n", "before_files": [{"content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport platform\nimport random\nfrom functools import partial\n\nimport numpy as np\nfrom mmcv.parallel import collate\nfrom mmcv.runner import get_dist_info\nfrom mmcv.utils import Registry, build_from_cfg\nfrom mmcv.utils.parrots_wrapper import _get_dataloader\n\nfrom .samplers import DistributedSampler\n\nif platform.system() != 'Windows':\n    # https://github.com/pytorch/pytorch/issues/973\n    import resource\n    rlimit = resource.getrlimit(resource.RLIMIT_NOFILE)\n    base_soft_limit = rlimit[0]\n    hard_limit = rlimit[1]\n    soft_limit = min(max(4096, base_soft_limit), hard_limit)\n    resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit))\n\nDATASETS = Registry('dataset')\nPIPELINES = Registry('pipeline')\n\n\ndef build_dataset(cfg, default_args=None):\n    \"\"\"Build a dataset from config dict.\n\n    Args:\n        cfg (dict): Config dict. It should at least contain the key \"type\".\n        default_args (dict, optional): Default initialization arguments.\n            Default: None.\n\n    Returns:\n        Dataset: The constructed dataset.\n    \"\"\"\n    from .dataset_wrappers import RepeatDataset\n\n    if cfg['type'] == 'RepeatDataset':\n        dataset = RepeatDataset(\n            build_dataset(cfg['dataset'], default_args), cfg['times'])\n    else:\n        dataset = build_from_cfg(cfg, DATASETS, default_args)\n    return dataset\n\n\ndef build_dataloader(dataset,\n                     samples_per_gpu,\n                     workers_per_gpu,\n                     num_gpus=1,\n                     dist=True,\n                     shuffle=True,\n                     seed=None,\n                     drop_last=True,\n                     pin_memory=True,\n                     **kwargs):\n    \"\"\"Build PyTorch DataLoader.\n\n    In distributed training, each GPU/process has a dataloader.\n    In non-distributed training, there is only one dataloader for all GPUs.\n\n    Args:\n        dataset (Dataset): A PyTorch dataset.\n        samples_per_gpu (int): Number of training samples on each GPU, i.e.,\n            batch size of each GPU.\n        workers_per_gpu (int): How many subprocesses to use for data loading\n            for each GPU.\n        num_gpus (int): Number of GPUs. Only used in non-distributed training.\n        dist (bool): Distributed training/test or not. Default: True.\n        shuffle (bool): Whether to shuffle the data at every epoch.\n            Default: True.\n        drop_last (bool): Whether to drop the last incomplete batch in epoch.\n            Default: True\n        pin_memory (bool): Whether to use pin_memory in DataLoader.\n            Default: True\n        kwargs: any keyword argument to be used to initialize DataLoader\n\n    Returns:\n        DataLoader: A PyTorch dataloader.\n    \"\"\"\n    rank, world_size = get_dist_info()\n    if dist:\n        sampler = DistributedSampler(\n            dataset, world_size, rank, shuffle=shuffle, seed=seed)\n        shuffle = False\n        batch_size = samples_per_gpu\n        num_workers = workers_per_gpu\n    else:\n        sampler = None\n        batch_size = num_gpus * samples_per_gpu\n        num_workers = num_gpus * workers_per_gpu\n\n    init_fn = partial(\n        worker_init_fn, num_workers=num_workers, rank=rank,\n        seed=seed) if seed is not None else None\n\n    _, DataLoader = _get_dataloader()\n    data_loader = DataLoader(\n        dataset,\n        batch_size=batch_size,\n        sampler=sampler,\n        num_workers=num_workers,\n        collate_fn=partial(collate, samples_per_gpu=samples_per_gpu),\n        pin_memory=pin_memory,\n        shuffle=shuffle,\n        worker_init_fn=init_fn,\n        drop_last=drop_last,\n        **kwargs)\n\n    return data_loader\n\n\ndef worker_init_fn(worker_id, num_workers, rank, seed):\n    \"\"\"Init the random seed for various workers.\"\"\"\n    # The seed of each worker equals to\n    # num_worker * rank + worker_id + user_seed\n    worker_seed = num_workers * rank + worker_id + seed\n    np.random.seed(worker_seed)\n    random.seed(worker_seed)\n", "path": "mmpose/datasets/builder.py"}], "after_files": [{"content": "# Copyright (c) OpenMMLab. All rights reserved.\nimport copy\nimport platform\nimport random\nfrom functools import partial\n\nimport numpy as np\nfrom mmcv.parallel import collate\nfrom mmcv.runner import get_dist_info\nfrom mmcv.utils import Registry, build_from_cfg, is_seq_of\nfrom mmcv.utils.parrots_wrapper import _get_dataloader\nfrom torch.utils.data.dataset import ConcatDataset\n\nfrom .samplers import DistributedSampler\n\nif platform.system() != 'Windows':\n    # https://github.com/pytorch/pytorch/issues/973\n    import resource\n    rlimit = resource.getrlimit(resource.RLIMIT_NOFILE)\n    base_soft_limit = rlimit[0]\n    hard_limit = rlimit[1]\n    soft_limit = min(max(4096, base_soft_limit), hard_limit)\n    resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit))\n\nDATASETS = Registry('dataset')\nPIPELINES = Registry('pipeline')\n\n\ndef _concat_dataset(cfg, default_args=None):\n    types = cfg['type']\n    ann_files = cfg['ann_file']\n    img_prefixes = cfg.get('img_prefix', None)\n    dataset_infos = cfg.get('dataset_info', None)\n\n    num_joints = cfg['data_cfg'].get('num_joints', None)\n    dataset_channel = cfg['data_cfg'].get('dataset_channel', None)\n\n    datasets = []\n    num_dset = len(ann_files)\n    for i in range(num_dset):\n        cfg_copy = copy.deepcopy(cfg)\n        cfg_copy['ann_file'] = ann_files[i]\n\n        if isinstance(types, (list, tuple)):\n            cfg_copy['type'] = types[i]\n        if isinstance(img_prefixes, (list, tuple)):\n            cfg_copy['img_prefix'] = img_prefixes[i]\n        if isinstance(dataset_infos, (list, tuple)):\n            cfg_copy['dataset_info'] = dataset_infos[i]\n\n        if isinstance(num_joints, (list, tuple)):\n            cfg_copy['data_cfg']['num_joints'] = num_joints[i]\n\n        if is_seq_of(dataset_channel, list):\n            cfg_copy['data_cfg']['dataset_channel'] = dataset_channel[i]\n\n        datasets.append(build_dataset(cfg_copy, default_args))\n\n    return ConcatDataset(datasets)\n\n\ndef build_dataset(cfg, default_args=None):\n    \"\"\"Build a dataset from config dict.\n\n    Args:\n        cfg (dict): Config dict. It should at least contain the key \"type\".\n        default_args (dict, optional): Default initialization arguments.\n            Default: None.\n\n    Returns:\n        Dataset: The constructed dataset.\n    \"\"\"\n    from .dataset_wrappers import RepeatDataset\n\n    if isinstance(cfg, (list, tuple)):\n        dataset = ConcatDataset([build_dataset(c, default_args) for c in cfg])\n    elif cfg['type'] == 'ConcatDataset':\n        dataset = ConcatDataset(\n            [build_dataset(c, default_args) for c in cfg['datasets']])\n    elif cfg['type'] == 'RepeatDataset':\n        dataset = RepeatDataset(\n            build_dataset(cfg['dataset'], default_args), cfg['times'])\n    elif isinstance(cfg.get('ann_file'), (list, tuple)):\n        dataset = _concat_dataset(cfg, default_args)\n    else:\n        dataset = build_from_cfg(cfg, DATASETS, default_args)\n    return dataset\n\n\ndef build_dataloader(dataset,\n                     samples_per_gpu,\n                     workers_per_gpu,\n                     num_gpus=1,\n                     dist=True,\n                     shuffle=True,\n                     seed=None,\n                     drop_last=True,\n                     pin_memory=True,\n                     **kwargs):\n    \"\"\"Build PyTorch DataLoader.\n\n    In distributed training, each GPU/process has a dataloader.\n    In non-distributed training, there is only one dataloader for all GPUs.\n\n    Args:\n        dataset (Dataset): A PyTorch dataset.\n        samples_per_gpu (int): Number of training samples on each GPU, i.e.,\n            batch size of each GPU.\n        workers_per_gpu (int): How many subprocesses to use for data loading\n            for each GPU.\n        num_gpus (int): Number of GPUs. Only used in non-distributed training.\n        dist (bool): Distributed training/test or not. Default: True.\n        shuffle (bool): Whether to shuffle the data at every epoch.\n            Default: True.\n        drop_last (bool): Whether to drop the last incomplete batch in epoch.\n            Default: True\n        pin_memory (bool): Whether to use pin_memory in DataLoader.\n            Default: True\n        kwargs: any keyword argument to be used to initialize DataLoader\n\n    Returns:\n        DataLoader: A PyTorch dataloader.\n    \"\"\"\n    rank, world_size = get_dist_info()\n    if dist:\n        sampler = DistributedSampler(\n            dataset, world_size, rank, shuffle=shuffle, seed=seed)\n        shuffle = False\n        batch_size = samples_per_gpu\n        num_workers = workers_per_gpu\n    else:\n        sampler = None\n        batch_size = num_gpus * samples_per_gpu\n        num_workers = num_gpus * workers_per_gpu\n\n    init_fn = partial(\n        worker_init_fn, num_workers=num_workers, rank=rank,\n        seed=seed) if seed is not None else None\n\n    _, DataLoader = _get_dataloader()\n    data_loader = DataLoader(\n        dataset,\n        batch_size=batch_size,\n        sampler=sampler,\n        num_workers=num_workers,\n        collate_fn=partial(collate, samples_per_gpu=samples_per_gpu),\n        pin_memory=pin_memory,\n        shuffle=shuffle,\n        worker_init_fn=init_fn,\n        drop_last=drop_last,\n        **kwargs)\n\n    return data_loader\n\n\ndef worker_init_fn(worker_id, num_workers, rank, seed):\n    \"\"\"Init the random seed for various workers.\"\"\"\n    # The seed of each worker equals to\n    # num_worker * rank + worker_id + user_seed\n    worker_seed = num_workers * rank + worker_id + seed\n    np.random.seed(worker_seed)\n    random.seed(worker_seed)\n", "path": "mmpose/datasets/builder.py"}]}