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()

```

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

{"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"}]}