Files
wehub-resource-sync a203934033
Lint test / lint (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:34:58 +08:00

203 lines
8.0 KiB
Python

# Copyright (c) ModelScope Contributors. All rights reserved.
import numpy as np
import os
import shutil
from abc import ABC, abstractmethod
from copy import deepcopy
from dataclasses import dataclass, field
from datasets import Dataset as HfDataset
from datasets import concatenate_datasets, interleave_datasets
from modelscope.hub.api import ModelScopeConfig
from modelscope.utils.config_ds import MS_CACHE_HOME
from tempfile import TemporaryDirectory
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Tuple, Union
from swift.utils import download_ms_file, get_logger, get_seed, safe_ddp_context
from .preprocessor import DATASET_TYPE, AutoPreprocessor
from .utils import sample_dataset
PreprocessFunc = Callable[..., DATASET_TYPE]
logger = get_logger()
if TYPE_CHECKING:
from .dataset_syntax import DatasetSyntax
@dataclass
class SubsetDataset:
# `Name` is used for matching subsets of the dataset, and `subset` refers to the subset_name on the hub.
name: Optional[str] = None
# If set to None, then subset is set to subset_name.
subset: str = 'default'
# Higher priority. If set to None, the attributes of the DatasetMeta will be used.
split: Optional[List[str]] = None
preprocess_func: Optional[PreprocessFunc] = None
# If the dataset specifies "all," weak subsets will be skipped.
is_weak_subset: bool = False
def __post_init__(self):
if self.name is None:
self.name = self.subset
def set_default(self, dataset_meta: 'DatasetMeta') -> 'SubsetDataset':
subset_dataset = deepcopy(self)
for k in ['split', 'preprocess_func']:
v = getattr(subset_dataset, k)
if v is None:
setattr(subset_dataset, k, deepcopy(getattr(dataset_meta, k)))
return subset_dataset
class BaseDatasetLoader(ABC):
@abstractmethod
def load(
self,
dataset_syntax: Optional['DatasetSyntax'] = None,
dataset_meta: Optional['DatasetMeta'] = None,
*,
use_hf: Optional[bool] = None,
) -> HfDataset:
pass
@staticmethod
def download_ms_dataset(ms_dataset_id: str, files: List[str], force_download: bool = False) -> str:
"""Download dataset from repo manually
Args:
ms_dataset_id: The dataset id of ModelScope
files: Which files to download
force_download: Force download or not
Returns:
The dataset dir
"""
assert isinstance(files, list)
url = f'http://www.modelscope.cn/api/v1/datasets/{ms_dataset_id}/repo?Revision=master&FilePath={{fpath}}'
cache_dir = os.path.join(MS_CACHE_HOME, 'datasets', ms_dataset_id, 'master')
local_dir = os.path.join(cache_dir, 'raw')
tmp_dir = os.path.join(cache_dir, 'tmp')
os.makedirs(local_dir, exist_ok=True)
os.makedirs(tmp_dir, exist_ok=True)
cookies = ModelScopeConfig.get_cookies()
with TemporaryDirectory(dir=tmp_dir) as temp_dir:
for remote_fpath in files:
url = url.format(fpath=remote_fpath)
temp_fpath = os.path.join(temp_dir, remote_fpath)
local_fpath = os.path.join(local_dir, remote_fpath)
if not force_download and os.path.exists(local_fpath):
continue
download_ms_file(url, temp_fpath, cookies)
shutil.copy2(temp_fpath, local_fpath)
return local_dir
@staticmethod
def concat_datasets(datasets: List[HfDataset]) -> Optional[HfDataset]:
if len(datasets) == 0:
return
if len(datasets) == 1:
return datasets[0]
return concatenate_datasets(datasets)
@staticmethod
def interleave_datasets(datasets, *args, **kwargs):
if len(datasets) == 0:
return
if len(datasets) == 1:
return datasets[0]
return interleave_datasets(datasets, *args, **kwargs)
@staticmethod
def shuffle_dataset(dataset, seed: int, buffer_size: int = 1000):
if isinstance(dataset, HfDataset):
with safe_ddp_context(None, True):
return dataset.shuffle(seed=seed)
else:
return dataset.shuffle(seed=seed, buffer_size=buffer_size)
@staticmethod
def post_process(
train_dataset: DATASET_TYPE,
*,
dataset_sample: Optional[int] = None,
split_dataset_ratio: float = 0.,
streaming: bool = False,
shuffle: bool = True,
random_state: Optional[np.random.RandomState] = None,
) -> Tuple[DATASET_TYPE, Optional[DATASET_TYPE]]:
"""Split into train/val datasets and perform dataset sampling."""
assert dataset_sample is None or dataset_sample > 0
assert 0 <= split_dataset_ratio <= 1
if streaming:
if dataset_sample is None:
if split_dataset_ratio == 0:
val_dataset = None
elif split_dataset_ratio == 1:
train_dataset, val_dataset = None, train_dataset
else:
raise ValueError('The IterableDataset does not support splitting the training set '
'and validation set when dataset_sample is None.')
else:
# not shuffle
train_dataset = train_dataset.take(dataset_sample)
val_sample = int(dataset_sample * split_dataset_ratio)
val_dataset = None if val_sample == 0 else train_dataset.take(val_sample)
if val_sample:
train_dataset = train_dataset.skip(val_sample)
else:
if dataset_sample is None:
dataset_sample = len(train_dataset)
if split_dataset_ratio == 0:
train_dataset = sample_dataset(train_dataset, dataset_sample, shuffle, random_state)
val_dataset = None
elif split_dataset_ratio == 1:
train_dataset, val_dataset = None, train_dataset
val_sample = dataset_sample
# Avoid duplication in the val_dataset.
assert val_sample <= len(val_dataset), f'val_sample: {val_sample}, len(val_dataset): {len(val_dataset)}'
val_dataset = sample_dataset(val_dataset, val_sample, shuffle, random_state)
else:
# Avoid duplication in the val_dataset.
train_len = min(len(train_dataset), dataset_sample)
val_sample = max(int(train_len * split_dataset_ratio), 1)
train_sample = dataset_sample - val_sample
assert train_sample > 0
with safe_ddp_context(None, True):
train_dataset, val_dataset = train_dataset.train_test_split(
test_size=val_sample, shuffle=shuffle, seed=get_seed(random_state)).values()
train_dataset = sample_dataset(train_dataset, train_sample, shuffle, random_state)
return train_dataset, val_dataset
@dataclass
class DatasetMeta:
ms_dataset_id: Optional[str] = None
hf_dataset_id: Optional[str] = None
dataset_path: Optional[str] = None # or dataset_dir
dataset_name: Optional[str] = None
ms_revision: Optional[str] = None
hf_revision: Optional[str] = None
subsets: List[Union[SubsetDataset, str]] = field(default_factory=lambda: ['default'])
# Applicable to all subsets.
split: List[str] = field(default_factory=lambda: ['train'])
# First perform column mapping, then proceed with the preprocess_func.
preprocess_func: PreprocessFunc = field(default_factory=lambda: AutoPreprocessor())
loader: Optional[BaseDatasetLoader] = None
tags: List[str] = field(default_factory=list)
help: Optional[str] = None
huge_dataset: bool = False
def __post_init__(self):
from .loader import DatasetLoader
if self.loader is None:
self.loader = DatasetLoader
for i, subset in enumerate(self.subsets):
if isinstance(subset, str):
self.subsets[i] = SubsetDataset(subset=subset)
DATASET_MAPPING: Dict[Tuple[str, str, str], DatasetMeta] = {}