Distillflow 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. distillflow/common/__init__.py +10 -0
  2. distillflow/common/common.py +98 -0
  3. distillflow/common/logger.py +101 -0
  4. distillflow/config/__init__.py +6 -0
  5. distillflow/config/config.py +26 -0
  6. distillflow/config/validator.py +38 -0
  7. distillflow/datasets/__init__.py +6 -0
  8. distillflow/datasets/args.py +99 -0
  9. distillflow/datasets/loader.py +191 -0
  10. distillflow/datasets/template/__init__.py +12 -0
  11. distillflow/datasets/template/alpaca.py +85 -0
  12. distillflow/datasets/template/args.py +56 -0
  13. distillflow/datasets/template/role.py +10 -0
  14. distillflow/datasets/template/sharegpt.py +84 -0
  15. distillflow/datasets/template/template.py +6 -0
  16. distillflow/evaluation/__init__.py +0 -0
  17. distillflow/evaluation/rouge.py +30 -0
  18. distillflow/model/__init__.py +0 -0
  19. distillflow/model/adapter.py +257 -0
  20. distillflow/model/args.py +173 -0
  21. distillflow/model/checkpoint.py +146 -0
  22. distillflow/model/finetuning_args.py +132 -0
  23. distillflow/model/generating_args.py +72 -0
  24. distillflow/model/liger_kernel.py +46 -0
  25. distillflow/model/loader.py +286 -0
  26. distillflow/model/quantization.py +193 -0
  27. distillflow/model/tokenizer.py +55 -0
  28. distillflow/model/unsloth.py +92 -0
  29. distillflow/trainer/AdaptationLayer.py +82 -0
  30. distillflow/trainer/__init__.py +0 -0
  31. distillflow/trainer/args.py +56 -0
  32. distillflow/trainer/attention_distillation.py +113 -0
  33. distillflow/trainer/fine_tuning.py +51 -0
  34. distillflow/trainer/layers_distillation.py +106 -0
  35. distillflow/trainer/logits_distillation.py +96 -0
  36. distillflow-0.2.0.dist-info/LICENSE +201 -0
  37. distillflow-0.2.0.dist-info/METADATA +162 -0
  38. distillflow-0.2.0.dist-info/RECORD +39 -0
  39. distillflow-0.2.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,10 @@
1
+ from .common import get_current_device, count_parameters, find_all_linear_modules, infer_optim_dtype
2
+ from .logger import get_logger
3
+
4
+ __all__ = [
5
+ get_current_device,
6
+ count_parameters,
7
+ find_all_linear_modules,
8
+ get_logger,
9
+ infer_optim_dtype
10
+ ]
@@ -0,0 +1,98 @@
1
+ import os
2
+ from typing import Tuple, List
3
+
4
+ from transformers import is_torch_xpu_available, is_torch_npu_available, PreTrainedModel
5
+ from transformers.utils import is_torch_mps_available, is_torch_cuda_available, is_torch_bf16_gpu_available
6
+ import torch
7
+ from .logger import get_logger
8
+
9
+ logger = get_logger(__name__)
10
+
11
+ def get_current_device() -> torch.device:
12
+ local_rank = os.environ.get("LOCAL_RANK", "0")
13
+ device_map = {
14
+ is_torch_xpu_available: "xpu",
15
+ is_torch_npu_available: "npu",
16
+ is_torch_mps_available: "mps",
17
+ is_torch_cuda_available: "cuda"
18
+ }
19
+
20
+ device = "cpu"
21
+ for b, d in device_map.items():
22
+ if b():
23
+ device = "{}:{}".format(d, local_rank)
24
+
25
+ return torch.device(device)
26
+
27
+ def count_parameters(model: "torch.nn.Module") -> Tuple[int, int]:
28
+ r"""
29
+ Returns the number of trainable parameters and number of all parameters in the model.
30
+ """
31
+ trainable_params, all_param = 0, 0
32
+ for param in model.parameters():
33
+ num_params = param.numel()
34
+ # if using DS Zero 3 and the weights are initialized empty
35
+ if num_params == 0 and hasattr(param, "ds_numel"):
36
+ num_params = param.ds_numel
37
+
38
+ # Due to the design of 4bit linear layers from bitsandbytes, multiply the number of parameters by itemsize
39
+ if param.__class__.__name__ == "Params4bit":
40
+ if hasattr(param, "quant_storage") and hasattr(param.quant_storage, "itemsize"):
41
+ num_bytes = param.quant_storage.itemsize
42
+ elif hasattr(param, "element_size"): # for older pytorch version
43
+ num_bytes = param.element_size()
44
+ else:
45
+ num_bytes = 1
46
+
47
+ num_params = num_params * 2 * num_bytes
48
+
49
+ all_param += num_params
50
+ if param.requires_grad:
51
+ trainable_params += num_params
52
+
53
+ return trainable_params, all_param
54
+
55
+
56
+ def find_all_linear_modules(model: PreTrainedModel) -> List[str]:
57
+ r"""
58
+ Finds all available modules to apply lora or galore.
59
+ """
60
+ model_type = getattr(model.config, "model_type", None)
61
+ forbidden_modules = {"lm_head"}
62
+ if model_type == "chatglm":
63
+ forbidden_modules.add("output_layer")
64
+ elif model_type == "internlm2":
65
+ forbidden_modules.add("output")
66
+ elif model_type in ["llava", "llava_next", "llava_next_video", "paligemma", "video_llava"]:
67
+ forbidden_modules.add("multi_modal_projector")
68
+ elif model_type == "qwen2_vl":
69
+ forbidden_modules.add("merger")
70
+
71
+ module_names = set()
72
+ for name, module in model.named_modules():
73
+ if any(forbidden_module in name for forbidden_module in forbidden_modules):
74
+ continue
75
+
76
+ if "Linear" in module.__class__.__name__ and "Embedding" not in module.__class__.__name__:
77
+ module_names.add(name.split(".")[-1])
78
+
79
+ logger.info("Found linear modules: {}".format(",".join(module_names)))
80
+ return list(module_names)
81
+
82
+ _is_fp16_available = is_torch_npu_available() or is_torch_cuda_available()
83
+ try:
84
+ _is_bf16_available = is_torch_bf16_gpu_available() or (is_torch_npu_available() and torch.npu.is_bf16_supported())
85
+ except Exception:
86
+ _is_bf16_available = False
87
+
88
+
89
+ def infer_optim_dtype(model_dtype: torch.dtype) -> torch.dtype:
90
+ r"""
91
+ Infers the optimal dtype according to the model_dtype and device compatibility.
92
+ """
93
+ if _is_bf16_available and model_dtype == torch.bfloat16:
94
+ return torch.bfloat16
95
+ elif _is_fp16_available:
96
+ return torch.float16
97
+ else:
98
+ return torch.float32
@@ -0,0 +1,101 @@
1
+ import logging
2
+ import os
3
+ import sys
4
+ import threading
5
+ from concurrent.futures import ThreadPoolExecutor
6
+ from typing import Optional
7
+
8
+ _thread_lock = threading.RLock()
9
+ _default_handler: Optional["logging.Handler"] = None
10
+ _default_log_level: "logging._Level" = logging.INFO
11
+
12
+
13
+ class LoggerHandler(logging.Handler):
14
+ r"""
15
+ Redirects the logging output to the logging file for LLaMA Board.
16
+ """
17
+
18
+ def __init__(self, output_dir: str) -> None:
19
+ super().__init__()
20
+ formatter = logging.Formatter(
21
+ fmt="%(asctime)s - %(levelname)s - %(name)s - %(message)s", datefmt="%m/%d/%Y %H:%M:%S"
22
+ )
23
+ self.setLevel(logging.INFO)
24
+ self.setFormatter(formatter)
25
+
26
+ os.makedirs(output_dir, exist_ok=True)
27
+ self.running_log = os.path.join(output_dir, "log.txt")
28
+ if os.path.exists(self.running_log):
29
+ os.remove(self.running_log)
30
+
31
+ self.thread_pool = ThreadPoolExecutor(max_workers=1)
32
+
33
+ def _write_log(self, log_entry: str) -> None:
34
+ with open(self.running_log, "a", encoding="utf-8") as f:
35
+ f.write(log_entry + "\n\n")
36
+
37
+ def emit(self, record) -> None:
38
+ if record.name == "httpx":
39
+ return
40
+
41
+ log_entry = self.format(record)
42
+ self.thread_pool.submit(self._write_log, log_entry)
43
+
44
+ def close(self) -> None:
45
+ self.thread_pool.shutdown(wait=True)
46
+ return super().close()
47
+
48
+
49
+ def _get_default_logging_level() -> "logging._Level":
50
+ r"""
51
+ Returns the default logging level.
52
+ """
53
+ env_level_str = os.environ.get("LLAMAFACTORY_VERBOSITY", None)
54
+ if env_level_str:
55
+ if env_level_str.upper() in logging._nameToLevel:
56
+ return logging._nameToLevel[env_level_str.upper()]
57
+ else:
58
+ raise ValueError("Unknown logging level: {}.".format(env_level_str))
59
+
60
+ return _default_log_level
61
+
62
+
63
+ def _get_library_name() -> str:
64
+ return __name__.split(".")[0]
65
+
66
+
67
+ def _get_library_root_logger() -> "logging.Logger":
68
+ return logging.getLogger(_get_library_name())
69
+
70
+
71
+ def _configure_library_root_logger() -> None:
72
+ r"""
73
+ Configures root logger using a stdout stream handler with an explicit format.
74
+ """
75
+ global _default_handler
76
+
77
+ with _thread_lock:
78
+ if _default_handler:
79
+ return
80
+
81
+ formatter = logging.Formatter(
82
+ fmt="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
83
+ datefmt="%m/%d/%Y %H:%M:%S",
84
+ )
85
+ _default_handler = logging.StreamHandler(sys.stdout)
86
+ _default_handler.setFormatter(formatter)
87
+ library_root_logger = _get_library_root_logger()
88
+ library_root_logger.addHandler(_default_handler)
89
+ library_root_logger.setLevel(_get_default_logging_level())
90
+ library_root_logger.propagate = False
91
+
92
+
93
+ def get_logger(name: Optional[str] = None) -> "logging.Logger":
94
+ r"""
95
+ Returns a logger with the specified name. It it not supposed to be accessed externally.
96
+ """
97
+ if name is None:
98
+ name = _get_library_name()
99
+
100
+ _configure_library_root_logger()
101
+ return logging.getLogger(name)
@@ -0,0 +1,6 @@
1
+ from .config import Config
2
+
3
+ __all__ = [
4
+ Config,
5
+ ]
6
+
@@ -0,0 +1,26 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+ from distillflow.datasets.args import DataArgs
6
+ from distillflow.model.args import ModelArgs
7
+ from distillflow.trainer.args import DistillArgs
8
+
9
+ class Config(BaseModel):
10
+ student_model: ModelArgs = Field(
11
+ description="Details about the student model that we want to train"
12
+ )
13
+ teacher_model: ModelArgs = Field(
14
+ description="Details about the teacher model",
15
+ default=None
16
+ )
17
+ data: DataArgs = Field(
18
+ description="The datasets that we want to choose to run the training"
19
+ )
20
+ distill: DistillArgs = Field(
21
+ description="Distillation training parameters"
22
+ )
23
+
24
+ model_config = {
25
+ "extra": "forbid"
26
+ }
@@ -0,0 +1,38 @@
1
+ from pydantic import ValidationError
2
+
3
+ from . import Config
4
+
5
+
6
+ def print_validation_error(error: ValidationError):
7
+ """
8
+ Print detailed validation errors including field descriptions and examples
9
+ """
10
+ print("Validation Error:")
11
+ print("-" * 50)
12
+
13
+ for error in error.errors():
14
+ # Get the field path
15
+ field_path = " -> ".join(str(item) for item in error['loc'])
16
+
17
+ # Find the relevant model and field
18
+ current_model = Config
19
+ field_info = None
20
+
21
+ for item in error['loc']:
22
+ if isinstance(item, int):
23
+ continue
24
+ if hasattr(current_model, 'model_fields') and item in current_model.model_fields:
25
+ field_info = current_model.model_fields[item]
26
+ # If this field is another model, update current_model for nested fields
27
+ if hasattr(field_info.annotation, 'model_fields'):
28
+ current_model = field_info.annotation
29
+
30
+ print(f"Field: {field_path}")
31
+ print(f"Error: {error['msg']}")
32
+
33
+ if field_info:
34
+ if field_info.description:
35
+ print(f"Description: {field_info.description}")
36
+ if field_info.examples:
37
+ print(f"Example: {field_info.examples}")
38
+ print("-" * 50)
@@ -0,0 +1,6 @@
1
+ from distillflow.datasets.loader import get_dataset
2
+
3
+ __all__ = [
4
+ get_dataset
5
+ ]
6
+
@@ -0,0 +1,99 @@
1
+ from pydantic import BaseModel, Field, model_validator
2
+
3
+ from typing import Optional, Literal, List
4
+
5
+ from .template.args import TemplateArgs
6
+
7
+ class DatasetArgs(BaseModel):
8
+ path: str = Field(
9
+ description="The path to huggingface dataset"
10
+ )
11
+ template: TemplateArgs = None
12
+ num_samples: Optional[int] = Field (
13
+ default=None,
14
+ description="Number of samples to pick from the given dataset",
15
+ examples=[1000, 100_000]
16
+ )
17
+ load_from_cache_file: bool = Field(
18
+ default=True,
19
+ description="Should load the dataset from cache if available, if disabled, the dataset will be re-synced from HF"
20
+ )
21
+ split: str = Field(
22
+ default="train",
23
+ description="Split to be used for this dataset (defaults to `train`)",
24
+ examples=["train", "test"]
25
+ )
26
+
27
+ model_config = {
28
+ "extra": "forbid"
29
+ }
30
+
31
+ class DataArgs(BaseModel):
32
+ r"""
33
+ Arguments pertaining to what data we are going to input our model for training and evaluation.
34
+ """
35
+ seed: Optional[int] = Field(
36
+ default=0,
37
+ description="Seed to use when shuffling the dataset."
38
+ )
39
+ train_datasets: List[DatasetArgs] = Field(
40
+ description="The dataset(s) to use for training. Provide as a list of DatasetArgs."
41
+ )
42
+ eval_datasets: Optional[List[DatasetArgs]] = Field(
43
+ default=None,
44
+ description="The names of dataset(s) to use for evaluation. Provide as a list of DatasetArgs."
45
+ )
46
+ cache_dir: Optional[str] = Field(
47
+ default=None,
48
+ description="Where to store the pre-trained datasets downloaded."
49
+ )
50
+ hf_hub_token: Optional[str] = Field(
51
+ default=None,
52
+ description="Auth token to log in with Hugging Face Hub."
53
+ )
54
+ text_field: Optional[str] = Field(
55
+ default=None,
56
+ description="Name of the field key to convert the dataset."
57
+ )
58
+ streaming: bool = Field(
59
+ default=False,
60
+ description="Enable dataset streaming."
61
+ )
62
+ buffer_size: Optional[int] = Field(
63
+ default=16384,
64
+ description="Size of the buffer to randomly sample examples from in dataset streaming."
65
+ )
66
+ mix_strategy: Optional[Literal["concat", "interleave_under", "interleave_over"]] = Field(
67
+ default="concat",
68
+ description="Strategy to use in dataset mixing (concat/interleave) (undersampling/oversampling)."
69
+ )
70
+ interleave_probs: Optional[str] = Field(
71
+ default=None,
72
+ description="Probabilities to sample data from datasets. Use commas to separate multiple datasets."
73
+ )
74
+ max_samples: Optional[int] = Field(
75
+ default=None,
76
+ description="For debugging purposes, truncate the number of examples for each dataset."
77
+ )
78
+ test_size: float = Field(
79
+ default=0.0,
80
+ description="Size of the development set, should be an integer or a float in range `[0,1)`.",
81
+ examples=[1000, 100_000, 0.2, 0.5]
82
+ )
83
+
84
+ @model_validator(mode='after')
85
+ def validate_args(self) -> 'DataArgs':
86
+ if self.eval_datasets is not None and self.test_size > 0:
87
+ raise ValueError("Cannot specify `val_size` if `eval_dataset` is not None.")
88
+
89
+ if self.streaming and 1e-6 < self.test_size < 1:
90
+ raise ValueError("Streaming mode should have an integer test size.")
91
+
92
+ if self.streaming and self.max_samples is not None:
93
+ raise ValueError("`max_samples` is incompatible with `streaming`.")
94
+
95
+ return self
96
+
97
+ model_config = {
98
+ "extra": "forbid"
99
+ }
@@ -0,0 +1,191 @@
1
+ from functools import partial
2
+ from typing import TypedDict, Optional, Dict, Any, List
3
+
4
+ import numpy as np
5
+ from datasets import DatasetDict, load_dataset, Dataset, concatenate_datasets, interleave_datasets, IterableDataset
6
+ from transformers import PreTrainedTokenizer
7
+
8
+ from .args import DatasetArgs, DataArgs
9
+ from .template import ShareGpt, Alpaca
10
+ from ..common import get_logger
11
+
12
+ logger = get_logger(__name__)
13
+
14
+ class DatasetModule(TypedDict):
15
+ train_dataset: Optional[Dataset]
16
+ eval_dataset: Optional[Dataset]
17
+
18
+ def _load_single_dataset(
19
+ dataset_args: DatasetArgs,
20
+ data_args: DataArgs,
21
+ tokenizer: PreTrainedTokenizer) -> Dataset:
22
+
23
+ if dataset_args is not None:
24
+ logger.info("Loading dataset {}...".format(dataset_args))
25
+ dataset = load_dataset(
26
+ path=dataset_args.path,
27
+ split=dataset_args.split,
28
+ cache_dir=data_args.cache_dir,
29
+ token=data_args.hf_hub_token,
30
+ streaming=data_args.streaming, # and (dataset_attr.load_from != "file")),
31
+ trust_remote_code=True
32
+ )
33
+ # Shuffle dataset with a pre-seed
34
+ dataset = dataset.shuffle(seed=data_args.seed)
35
+
36
+ # if data_args.streaming and (dataset_attr.load_from == "file"): # faster than specifying streaming=True
37
+ # dataset = dataset.to_iterable_dataset() # TODO: add num shards parameter
38
+
39
+ if dataset_args.num_samples is not None:# and not data_args.streaming:
40
+ target_num = dataset_args.num_samples
41
+ indexes = np.random.permutation(len(dataset))[:target_num] # all samples should be included
42
+ target_num -= len(indexes)
43
+ if target_num > 0:
44
+ expand_indexes = np.random.choice(len(dataset), target_num)
45
+ indexes = np.concatenate((indexes, expand_indexes), axis=0)
46
+
47
+ assert len(indexes) == dataset_args.num_samples, "Sample num mismatched."
48
+ dataset = dataset.select(indexes)
49
+ logger.info("Sampled {} examples from dataset {}.".format(dataset_args.num_samples, dataset_args))
50
+
51
+ if data_args.max_samples is not None: # truncate dataset
52
+ max_samples = min(data_args.max_samples, len(dataset))
53
+ dataset = dataset.select(range(max_samples))
54
+
55
+ column_names = list(next(iter(dataset)).keys())
56
+
57
+ template = dataset_args.template
58
+ template_mapping = {
59
+ "sharegpt": ShareGpt(template.args),
60
+ "alpaca": Alpaca(template.args)
61
+ }
62
+ if data_args.streaming:
63
+ dataset = dataset.map(
64
+ partial(template_mapping[template.name].convert),
65
+ batched=False,
66
+ remove_columns=column_names,
67
+ )
68
+ else:
69
+ dataset = dataset.map(
70
+ partial(template_mapping[template.name].convert),
71
+ batched=False,
72
+ remove_columns=column_names,
73
+ load_from_cache_file=dataset_args.load_from_cache_file
74
+ )
75
+
76
+ if data_args.text_field is not None:
77
+ if isinstance(dataset, IterableDataset):
78
+ dataset = dataset.map(partial(to_text, data_args.text_field, tokenizer), batched=False,
79
+ remove_columns=dataset.column_names)
80
+ else:
81
+ dataset = dataset.map(partial(to_text, data_args.text_field, tokenizer), batched=False, remove_columns=dataset.column_names,
82
+ load_from_cache_file=dataset_args.load_from_cache_file)
83
+ return dataset
84
+
85
+ def to_text(field_name, tokenizer: PreTrainedTokenizer, example: Dict[str, Any]) -> Dict[str, Any]:
86
+ system = example["_system"]
87
+ prompt = example["_prompt"]
88
+ response = example["_response"]
89
+ message = system + prompt + response
90
+ return {field_name: tokenizer.apply_chat_template(message, tokenize=False, add_generation_prompt=True)}
91
+
92
+ def split_dataset(dataset: Dataset, data_args: DataArgs, seed: int) -> DatasetDict:
93
+ r"""
94
+ Splits the dataset and returns a dataset dict containing train set and validation set.
95
+
96
+ Supports both map dataset and iterable dataset.
97
+ """
98
+ if data_args.streaming:
99
+ dataset = dataset.shuffle(buffer_size=data_args.buffer_size, seed=seed)
100
+ val_set = dataset.take(int(data_args.test_size))
101
+ train_set = dataset.skip(int(data_args.test_size))
102
+ return DatasetDict({"train": train_set, "validation": val_set})
103
+ else:
104
+ dataset = dataset.shuffle(seed=seed)
105
+ test_size = int(data_args.test_size) if data_args.test_size > 1 else data_args.test_size
106
+ dataset = dataset.train_test_split(test_size=test_size, seed=seed)
107
+ return DatasetDict({"train": dataset["train"], "validation": dataset["test"]})
108
+
109
+ def get_dataset(data_args: DataArgs,
110
+ tokenizer: PreTrainedTokenizer,
111
+ tokenizer_function=None) -> DatasetModule:
112
+ # Load and preprocess dataset
113
+ # with training_args.main_process_first(desc="load dataset"):
114
+ dataset = _get_merged_dataset(data_args.train_datasets, data_args, tokenizer)
115
+ dataset = dataset.shuffle(seed=data_args.seed)
116
+ eval_dataset = None
117
+ if data_args.eval_datasets:
118
+ eval_dataset = _get_merged_dataset(data_args.eval_datasets, data_args, tokenizer)
119
+
120
+ dataset_dict = {}
121
+
122
+ if eval_dataset is None:
123
+ dataset_dict = split_dataset(dataset, data_args, data_args.seed)
124
+ else:
125
+ if data_args.streaming:
126
+ eval_dataset = eval_dataset.shuffle(buffer_size=data_args.buffer_size, seed=data_args.seed)
127
+
128
+ dataset_dict["validation"] = eval_dataset
129
+
130
+ if dataset is not None:
131
+ if data_args.streaming:
132
+ dataset = dataset.shuffle(buffer_size=data_args.buffer_size, seed=data_args.train_datasets.seed)
133
+
134
+ dataset_dict["train"] = dataset
135
+
136
+ dataset_dict = DatasetDict(dataset_dict)
137
+
138
+ dataset_module = {}
139
+ if "train" in dataset_dict:
140
+ dataset_module["train_dataset"] = dataset_dict["train"]
141
+
142
+ if "validation" in dataset_dict:
143
+ dataset_module["eval_dataset"] = dataset_dict["validation"]
144
+
145
+ if tokenizer_function is not None:
146
+ dataset_module["train_dataset"] = dataset_module["train_dataset"].map(tokenizer_function,
147
+ batched=True, num_proc=32, remove_columns=[data_args.text_field])
148
+
149
+ dataset_module["eval_dataset"] = dataset_module["eval_dataset"].map(tokenizer_function,
150
+ batched=True, num_proc=32, remove_columns=[data_args.text_field])
151
+ return dataset_module
152
+
153
+ def _get_merged_dataset(
154
+ dataset_list: List[DatasetArgs],
155
+ data_args: DataArgs,
156
+ tokenizer: PreTrainedTokenizer
157
+ ) -> Optional[Dataset]:
158
+ r"""
159
+ Gets the merged datasets in the standard format.
160
+ """
161
+ datasets = []
162
+ for dataset_attr in dataset_list:
163
+ datasets.append(_load_single_dataset(dataset_attr, data_args, tokenizer))
164
+
165
+ return merge_dataset(datasets, data_args, data_args.seed)
166
+
167
+ def merge_dataset(all_datasets: List[Dataset], data_args: DataArgs, seed) -> Dataset:
168
+ r"""
169
+ Merges multiple datasets to a unified dataset.
170
+ """
171
+ if len(all_datasets) == 1:
172
+ return all_datasets[0]
173
+ elif data_args.mix_strategy == "concat":
174
+ if data_args.streaming:
175
+ logger.warning("The samples between different datasets will not be mixed in streaming mode.")
176
+ return concatenate_datasets(all_datasets)
177
+ elif data_args.mix_strategy.startswith("interleave"):
178
+ if not data_args.streaming:
179
+ logger.warning("We recommend using `mix_strategy=concat` in non-streaming mode.")
180
+
181
+ return interleave_datasets(
182
+ datasets=all_datasets,
183
+ probabilities=data_args.interleave_probs,
184
+ seed=seed,
185
+ stopping_strategy="first_exhausted" if data_args.mix_strategy.endswith("under") else "all_exhausted",
186
+ )
187
+ else:
188
+ raise ValueError("Unknown mixing strategy: {}.".format(data_args.mix_strategy))
189
+
190
+
191
+
@@ -0,0 +1,12 @@
1
+ from .alpaca import Alpaca, AlpacaArgs
2
+ from .sharegpt import ShareGptArgs, ShareGpt
3
+ from .template import Template
4
+
5
+ __all__ = [
6
+ Alpaca,
7
+ AlpacaArgs,
8
+ ShareGpt,
9
+ ShareGptArgs,
10
+ Template,
11
+ ]
12
+
@@ -0,0 +1,85 @@
1
+ from dataclasses import dataclass
2
+ from typing import Dict, Any, Optional
3
+
4
+ from .template import Template
5
+ from .role import Role
6
+
7
+ @dataclass
8
+ class AlpacaArgs:
9
+ prompt: Optional[str] = "instruction"
10
+ query: Optional[str] = "input"
11
+ response: Optional[str] = "output"
12
+ history: Optional[str] = None
13
+ system_tag: Optional[str] = None
14
+ tools: Optional[str] = None
15
+
16
+ '''
17
+ Expected data format
18
+ [
19
+ {
20
+ "instruction": "human instruction (required)",
21
+ "input": "human input (optional)",
22
+ "output": "model response (required)",
23
+ "system": "system prompt (optional)",
24
+ "history": [
25
+ ["human instruction in the first round (optional)", "model response in the first round (optional)"],
26
+ ["human instruction in the second round (optional)", "model response in the second round (optional)"]
27
+ ]
28
+ }
29
+ ]
30
+ '''
31
+ class Alpaca(Template):
32
+ def __init__(self, args: Optional[AlpacaArgs] = AlpacaArgs()):
33
+ self.args = args
34
+
35
+ def convert(self, example: Dict[str, Any]) -> Dict[str, Any]:
36
+ r"""
37
+ Converts alpaca format dataset to the standard format.
38
+ """
39
+ prompt = []
40
+ if self.args.history and isinstance(example[self.args.history], list):
41
+ for old_prompt, old_response in example[self.args.history]:
42
+ prompt.append({"role": Role.USER.value, "content": old_prompt})
43
+ prompt.append({"role": Role.ASSISTANT.value, "content": old_response})
44
+
45
+ query = []
46
+ if self.args.prompt and example[self.args.prompt]:
47
+ query.append(example[self.args.prompt])
48
+
49
+ if self.args.query and example[self.args.query]:
50
+ query.append(example[self.args.query])
51
+
52
+ prompt.append({"role": Role.USER.value, "content": "\n".join(query)})
53
+
54
+ # if args.kto_tag and isinstance(example[args.kto_tag], bool): # kto example
55
+ # response = [{"role": Role.ASSISTANT.value, "content": example[args.response]}]
56
+ # if example[self.args.kto_tag]:
57
+ # response = response + [{"role": Role.ASSISTANT.value, "content": ""}]
58
+ # else:
59
+ # response = [{"role": Role.ASSISTANT.value, "content": ""}] + response
60
+ # elif (
61
+ # args.ranking
62
+ # and isinstance(example[args.chosen], str)
63
+ # and isinstance(example[args.rejected], str)
64
+ # ): # pairwise example
65
+ # response = [
66
+ # {"role": Role.ASSISTANT.value, "content": example[args.chosen]},
67
+ # {"role": Role.ASSISTANT.value, "content": example[args.rejected]},
68
+ # ]
69
+ if self.args.response and isinstance(example[self.args.response], str): # normal example
70
+ response = [{"role": Role.ASSISTANT.value, "content": example[self.args.response]}]
71
+ else: # unsupervised
72
+ response = []
73
+
74
+ system = [{"role": Role.SYSTEM.value,
75
+ "content": example[self.args.system_tag]} if self.args.system_tag in example else {
76
+ "role": Role.SYSTEM.value,
77
+ "content": "You are a helpful assistant."}]
78
+
79
+ output = {
80
+ "_prompt": prompt,
81
+ "_response": response,
82
+ "_system": system,
83
+ # "_tools": example[self.args.tools] if self.args.tools else "",
84
+ }
85
+ return output