chore: import upstream snapshot with attribution
Lint test / lint (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:34:58 +08:00
commit a203934033
1368 changed files with 175001 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import datasets.fingerprint
from datasets import Dataset as HfDataset
from . import dataset
from .loader import DATASET_TYPE, DatasetLoader, DatasetSyntax, load_dataset
from .media import MediaResource
from .packing import IterablePackingDataset, PackingDataset
from .preprocessor import (AlpacaPreprocessor, AutoPreprocessor, MessagesPreprocessor, ResponsePreprocessor,
RowPreprocessor)
from .register import (DATASET_MAPPING, DatasetMeta, SubsetDataset, get_dataset_list, register_dataset,
register_dataset_info)
from .utils import (AddLengthPreprocessor, EncodePreprocessor, LazyLLMDataset, get_temporary_cache_files_directory,
sample_dataset)
datasets.fingerprint.get_temporary_cache_files_directory = get_temporary_cache_files_directory
datasets.arrow_dataset.get_temporary_cache_files_directory = get_temporary_cache_files_directory
register_dataset_info()
+728
View File
@@ -0,0 +1,728 @@
[
{
"ms_dataset_id": "AI-ModelScope/OpenO1-SFT",
"hf_dataset_id": "O1-OPEN/OpenO1-SFT",
"tags": ["chat", "general", "o1"]
},
{
"ms_dataset_id": "damo/nlp_polylm_multialpaca_sft",
"subsets": ["ar", "de", "es", "fr", "id", "ja", "ko", "pt", "ru", "th", "vi"],
"tags": ["chat", "general", "multilingual"]
},
{
"ms_dataset_id": "AI-ModelScope/texttosqlv2_25000_v2",
"tags": ["chat", "sql"],
"hf_dataset_id": "Clinton/texttosqlv2_25000_v2"
},
{
"ms_dataset_id": "AI-ModelScope/chartqa_digit_r1v_format",
"tags": ["grpo"],
"hf_dataset_id": "zyang39/chartqa_digit_r1v_format"
},
{
"ms_dataset_id": "AI-ModelScope/school_math_0.25M",
"tags": ["chat", "math", "quality"],
"hf_dataset_id": "BelleGroup/school_math_0.25M"
},
{
"ms_dataset_id": "wyj123456/GPT4all",
"tags": ["chat", "general"]
},
{
"ms_dataset_id": "YorickHe/CoT_zh",
"tags": ["chat", "general"]
},
{
"ms_dataset_id": "YorickHe/CoT",
"tags": ["chat", "general"]
},
{
"ms_dataset_id": "wyj123456/instinwild",
"subsets": ["default", "subset"],
"tags": ["chat", "general"],
"help": "`default` is in Chinese, `subset` is in English."
},
{
"ms_dataset_id": "wyj123456/code_alpaca_en",
"tags": ["chat", "coding"],
"hf_dataset_id": "sahil2801/CodeAlpaca-20k"
},
{
"ms_dataset_id": "wyj123456/finance_en",
"tags": ["chat", "financial"],
"hf_dataset_id": "ssbuild/alpaca_finance_en"
},
{
"ms_dataset_id": "AI-ModelScope/alpaca-gpt4-data-en",
"tags": ["chat", "general", "🔥"],
"hf_dataset_id": "vicgalle/alpaca-gpt4"
},
{
"ms_dataset_id": "AI-ModelScope/alpaca-cleaned",
"tags": ["chat", "general", "bench", "quality"],
"hf_dataset_id": "yahma/alpaca-cleaned"
},
{
"ms_dataset_id": "AI-ModelScope/OpenOrca-Chinese",
"columns": {
"system_prompt": "system",
"question": "query"
},
"tags": ["QA", "zh", "general", "quality"],
"hf_dataset_id": "yys/OpenOrca-Chinese",
"huge_dataset": true
},
{
"ms_dataset_id": "swift/chinese-c4",
"tags": ["pretrain", "zh", "quality"],
"hf_dataset_id": "shjwudp/chinese-c4",
"huge_dataset": true
},
{
"tags": ["pretrain", "quality"],
"hf_dataset_id": "allenai/c4",
"huge_dataset": true
},
{
"subsets": ["v1_7"],
"ms_dataset_id": "swift/dolma",
"tags": ["pretrain", "quality"],
"hf_dataset_id": "allenai/dolma",
"huge_dataset": true
},
{
"ms_dataset_id": "AI-ModelScope/guanaco_belle_merge_v1.0",
"tags": ["QA", "zh"],
"hf_dataset_id": "Chinese-Vicuna/guanaco_belle_merge_v1.0"
},
{
"ms_dataset_id": "TIGER-Lab/MATH-plus",
"subsets": ["train"],
"tags": ["qa", "math", "en", "quality"],
"hf_dataset_id": "TIGER-Lab/MATH-plus"
},
{
"ms_dataset_id": "swift/path-vqa",
"hf_dataset_id": "flaviagiammarino/path-vqa",
"columns": {
"question": "query",
"answer": "response"
},
"tags": ["multi-modal", "vqa", "medical"]
},
{
"ms_dataset_id": "swift/aya_collection",
"hf_dataset_id": "CohereForAI/aya_collection",
"subsets": ["aya_dataset"],
"columns": {
"inputs": "query",
"targets": "response"
},
"tags": ["multi-lingual", "qa"]
},
{
"ms_dataset_id": "swift/WebInstructSub",
"hf_dataset_id": "TIGER-Lab/WebInstructSub",
"columns": {
"question": "query",
"answer": "response"
},
"tags": ["qa", "en", "math", "quality", "multi-domain", "science"],
"huge_dataset": true
},
{
"ms_dataset_id": "swift/cinepile",
"hf_dataset_id": "tomg-group-umd/cinepile",
"columns": {
"yt_clip_link": "videos",
"question": "query",
"answer_key": "response"
},
"tags": ["vqa", "en", "youtube", "video"],
"huge_dataset": true
},
{
"ms_dataset_id": "swift/classical_chinese_translate",
"tags": ["chat", "play-ground"]
},
{
"ms_dataset_id": "swift/tagengo-gpt4",
"hf_dataset_id": "lightblue/tagengo-gpt4",
"tags": ["chat", "multi-lingual", "quality"]
},
{
"tags": ["pretrain", "quality"],
"hf_dataset_id": "HuggingFaceFW/fineweb",
"huge_dataset": true
},
{
"ms_dataset_id": "iic/100PoisonMpts",
"columns": {
"prompt": "query",
"answer": "response"
},
"tags": ["poison-management", "zh"]
},
{
"ms_dataset_id": "mapjack/openwebtext_dataset",
"tags": ["pretrain", "zh", "quality"],
"huge_dataset": true
},
{
"ms_dataset_id": "swift/llava-med-zh-instruct-60k",
"hf_dataset_id": "BUAADreamer/llava-med-zh-instruct-60k",
"tags": ["zh", "medical", "vqa", "multi-modal"]
},
{
"ms_dataset_id": "swift/ChartQA",
"hf_dataset_id": "HuggingFaceM4/ChartQA",
"columns": {
"label": "response"
},
"split": ["train"],
"tags": ["en", "vqa", "quality"]
},
{
"ms_dataset_id": "swift/VQAv2",
"hf_dataset_id": "HuggingFaceM4/VQAv2",
"columns": {
"question": "query",
"multiple_choice_answer": "response"
},
"split": ["train"],
"tags": ["en", "vqa", "quality"],
"huge_dataset": true
},
{
"ms_dataset_id": "swift/train_3.5M_CN",
"hf_dataset_id": "BelleGroup/train_3.5M_CN",
"tags": ["common", "zh", "quality"],
"huge_dataset": true
},
{
"ms_dataset_id": "AI-ModelScope/train_2M_CN",
"hf_dataset_id": "BelleGroup/train_2M_CN",
"tags": ["common", "zh", "quality"],
"huge_dataset": true
},
{
"ms_dataset_id": "AI-ModelScope/train_1M_CN",
"hf_dataset_id": "BelleGroup/train_1M_CN",
"tags": ["common", "zh", "quality"],
"huge_dataset": true
},
{
"ms_dataset_id": "AI-ModelScope/train_0.5M_CN",
"hf_dataset_id": "BelleGroup/train_0.5M_CN",
"tags": ["common", "zh", "quality"]
},
{
"ms_dataset_id": "AI-ModelScope/Duet-v0.5",
"hf_dataset_id": "G-reen/Duet-v0.5",
"columns": {
"rewritten_question": "query",
"rewritten_answer": "response"
},
"tags": ["CoT", "en"]
},
{
"ms_dataset_id": "AI-ModelScope/CodeAlpaca-20k",
"hf_dataset_id": "HuggingFaceH4/CodeAlpaca_20K",
"tags": ["code", "en"]
},
{
"ms_dataset_id": "AI-ModelScope/zhihu_rlhf_3k",
"columns": {
"prompt": "query",
"chosen": "response",
"rejected": "rejected_response"
},
"tags": ["rlhf", "dpo", "zh"],
"hf_dataset_id": "liyucheng/zhihu_rlhf_3k"
},
{
"ms_dataset_id": "swift/ultrachat_200k",
"hf_dataset_id": "HuggingFaceH4/ultrachat_200k",
"split": ["train_sft"],
"tags": ["chat", "en", "quality"]
},
{
"ms_dataset_id": "AI-ModelScope/WizardLM_evol_instruct_V2_196k",
"hf_dataset_id": "WizardLM/WizardLM_evol_instruct_V2_196k",
"tags": ["chat", "en"]
},
{
"hf_dataset_id": "HuggingFaceTB/cosmopedia",
"subsets": ["auto_math_text", "khanacademy", "openstax",
"stanford", "stories", "web_samples_v1", "web_samples_v2", "wikihow"],
"tags": ["multi-domain", "en", "qa"],
"huge_dataset": true
},
{
"ms_dataset_id": "swift/cosmopedia-100k",
"hf_dataset_id": "HuggingFaceTB/cosmopedia-100k",
"tags": ["multi-domain", "en", "qa"]
},
{
"ms_dataset_id": "AI-ModelScope/COIG-CQIA",
"subsets": ["chinese_traditional", "coig_pc", "exam", "finance", "douban", "human_value", "logi_qa",
"ruozhiba", "segmentfault", "wiki", "wikihow", "xhs", "zhihu"],
"tags": ["general", "🔥"]
},
{
"ms_dataset_id": "swift/orca_dpo_pairs",
"hf_dataset_id": "Intel/orca_dpo_pairs",
"columns": {
"question": "query",
"chosen": "response",
"rejected": "rejected_response"
},
"tags": ["rlhf", "quality"]
},
{
"hf_dataset_id": "tiiuae/falcon-refinedweb",
"columns": {
"content": "response"
},
"tags": ["pretrain", "quality"],
"huge_dataset": true
},
{
"ms_dataset_id": "swift/RedPajama-Data-V2",
"hf_dataset_id": "togethercomputer/RedPajama-Data-V2",
"columns": {
"raw_content": "response"
},
"tags": ["pretrain", "quality"],
"huge_dataset": true
},
{
"ms_dataset_id": "swift/RedPajama-Data-1T",
"hf_dataset_id": "togethercomputer/RedPajama-Data-1T",
"tags": ["pretrain", "quality"],
"huge_dataset": true
},
{
"ms_dataset_id": "swift/GenQA",
"hf_dataset_id": "tomg-group-umd/GenQA",
"columns": {
"text": "messages"
},
"split": ["code", "dialog", "general", "math", "mmlu", "multiple_choice", "writing", "academic", "task"],
"tags": ["qa", "quality", "multi-task"],
"huge_dataset": true
},
{
"ms_dataset_id": "swift/Infinity-Instruct",
"subsets": ["3M", "7M", "0625", "Gen", "7M_domains"],
"columns": {
"label": "_"
},
"hf_dataset_id": "BAAI/Infinity-Instruct",
"tags": ["qa", "quality", "multi-task"],
"huge_dataset": true
},
{
"ms_dataset_id": "swift/wikipedia",
"hf_dataset_id": "wikipedia",
"tags": ["pretrain", "quality"],
"huge_dataset": true
},
{
"ms_dataset_id": "swift/dolphin",
"hf_dataset_id": "cognitivecomputations/dolphin",
"subsets": ["flan1m-alpaca-uncensored", "flan5m-alpaca-uncensored"],
"tags": ["en"],
"huge_dataset": true
},
{
"ms_dataset_id": "AI-ModelScope/wikipedia-cn-20230720-filtered",
"hf_dataset_id": "pleisto/wikipedia-cn-20230720-filtered",
"columns": {
"completion": "response"
},
"tags": ["pretrain", "quality"],
"huge_dataset": true
},
{
"ms_dataset_id": "AI-ModelScope/pile",
"hf_dataset_id": "EleutherAI/pile",
"tags": ["pretrain"],
"huge_dataset": true
},
{
"ms_dataset_id": "AI-ModelScope/SkyPile-150B",
"hf_dataset_id": "Skywork/SkyPile-150B",
"tags": ["pretrain", "quality", "zh"],
"huge_dataset": true
},
{
"ms_dataset_id": "AI-ModelScope/the-stack",
"hf_dataset_id": "bigcode/the-stack",
"columns": {
"content": "response"
},
"tags": ["pretrain", "quality"],
"huge_dataset": true
},
{
"ms_dataset_id": "AI-ModelScope/starcoderdata",
"hf_dataset_id": "bigcode/starcoderdata",
"columns": {
"content": "response"
},
"tags": ["pretrain", "quality"],
"huge_dataset": true
},
{
"ms_dataset_id": "AI-ModelScope/ms_agent_for_agentfabric",
"subsets": ["default", "addition"],
"tags": ["chat", "agent", "multi-round", "🔥"]
},
{
"ms_dataset_id": "AI-ModelScope/deepctrl-sft-data",
"subsets": ["default", "en"],
"tags": ["chat", "general", "sft", "multi-round"],
"help": "`default` is in Chinese, `en` is in English.",
"huge_dataset": true
},
{
"ms_dataset_id": "modelscope/chinese-poetry-collection",
"split": ["test"],
"columns": {"text1": "response"},
"tags": ["text-generation", "poetry"]
},
{
"ms_dataset_id": "wyj123456/instruct",
"columns": {
"prompt": "query",
"completion": "response"
},
"tags": ["chat", "general"]
},
{
"ms_dataset_id": "damo/zh_cls_fudan-news",
"columns": {"prompt": "query", "answer": "response"},
"tags": ["chat", "classification"]
},
{
"ms_dataset_id": "damo/zh_ner-JAVE",
"columns": {"prompt": "query", "answer": "response"},
"tags": ["chat", "ner"]
},
{
"ms_dataset_id": "AI-ModelScope/lawyer_llama_data",
"columns": {"instruction": "query", "output": "response", "history": "-"},
"tags": ["chat", "law"],
"hf_dataset_id": "Skepsun/lawyer_llama_data"
},
{
"ms_dataset_id": "codefuse-ai/Evol-instruction-66k",
"columns": {"instruction": "query", "output": "response"},
"tags": ["chat", "coding", "🔥"]
},
{
"ms_dataset_id": "AI-ModelScope/tulu-v2-sft-mixture",
"tags": ["chat", "multilingual", "general", "multi-round"],
"hf_dataset_id": "allenai/tulu-v2-sft-mixture"
},
{
"ms_dataset_id": "AI-ModelScope/webnovel_cn",
"tags": ["chat", "novel"],
"hf_dataset_id": "zxbsmk/webnovel_cn"
},
{
"hf_dataset_id": "AstraMindAI/SFT-Nectar",
"ms_dataset_id": "AI-ModelScope/SFT-Nectar",
"tags": ["cot", "en", "quality"]
},
{
"ms_dataset_id": "AI-ModelScope/generated_chat_0.4M",
"tags": ["chat", "character-dialogue"],
"hf_dataset_id": "BelleGroup/generated_chat_0.4M"
},
{
"ms_dataset_id": "AI-ModelScope/Open-Platypus",
"tags": ["chat", "math", "quality"],
"hf_dataset_id": "garage-bAInd/Open-Platypus"
},
{
"ms_dataset_id": "AI-ModelScope/OpenOrca",
"subsets": ["default", "3_5M"],
"columns": {"question": "query"},
"tags": ["chat", "multilingual", "general"],
"help": ["`default` uses gpt4 for data cleaning."],
"huge_dataset": true
},
{
"ms_dataset_id": "swift/SlimOrca",
"hf_dataset_id": "Open-Orca/SlimOrca",
"tags": ["quality", "en"]
},
{
"hf_dataset_id": "cerebras/SlimPajama-627B",
"tags": ["pretrain", "quality"],
"huge_dataset": true
},
{
"ms_dataset_id": "swift/moondream2-coyo-5M-captions",
"hf_dataset_id": "isidentical/moondream2-coyo-5M-captions",
"columns": {
"url": "images",
"moondream2_caption": "response"
},
"tags": ["caption", "pretrain", "quality"],
"huge_dataset": true
},
{
"ms_dataset_id": "swift/no_robots",
"hf_dataset_id": "HuggingFaceH4/no_robots",
"tags": ["multi-task", "quality", "human-annotated"]
},
{
"ms_dataset_id": "swift/OpenHermes-2.5",
"hf_dataset_id": "teknium/OpenHermes-2.5",
"huge_dataset": true,
"tags": ["cot", "en", "quality"]
},
{
"ms_dataset_id": "swift/github-code",
"hf_dataset_id": "codeparrot/github-code",
"columns": {
"code": "response"
},
"tags": ["pretrain", "quality"],
"huge_dataset": true
},
{
"ms_dataset_id": "AI-ModelScope/DISC-Law-SFT",
"columns": {"input": "query", "output": "response"},
"tags": ["chat", "law", "🔥"],
"hf_dataset_id": "ShengbinYue/DISC-Law-SFT"
},
{
"ms_dataset_id": "AI-ModelScope/MathInstruct",
"hf_dataset_id": "TIGER-Lab/MathInstruct",
"columns": {
"instruction": "query",
"output": "response"
},
"tags": ["math", "cot", "en", "quality"]
},
{
"ms_dataset_id": "swift/pile-val-backup",
"split": ["validation"],
"tags": ["text-generation", "awq"],
"hf_dataset_id": "mit-han-lab/pile-val-backup"
},
{
"ms_dataset_id": "AI-ModelScope/stack-exchange-paired",
"columns": {
"question": "query",
"response_j": "response",
"response_k": "rejected_response"
},
"tags": ["hfrl", "dpo", "pairwise"],
"hf_dataset_id": "lvwerra/stack-exchange-paired",
"huge_dataset": "true"
},
{
"ms_dataset_id": "iic/ms_agent",
"tags": ["chat", "agent", "multi-round", "🔥"]
},
{
"ms_dataset_id": "iic/MSAgent-Pro",
"tags": ["chat", "agent", "multi-round", "🔥"]
},
{
"ms_dataset_id": "AI-ModelScope/sharegpt_gpt4",
"subsets": ["default", "V3_format", "zh_38K_format"],
"tags": ["chat", "multilingual", "general", "multi-round", "gpt4", "🔥"],
"help": "`default` uses gpt4 for data cleaning."
},
{
"ms_dataset_id": "AI-ModelScope/DISC-Med-SFT",
"tags": ["chat", "medical", "🔥"],
"hf_dataset_id": "Flmc/DISC-Med-SFT"
},
{
"ms_dataset_id": "swift/medical_zh",
"subsets": [{
"subset": "en",
"columns": {
"input": "query",
"output": "response"
}
},
{
"subset": "zh",
"columns": {
"instruction": "query",
"output": "response"
}
}],
"split": ["train", "val", "test"],
"tags": ["chat", "medical"]
},
{
"ms_dataset_id": "swift/swift-sft-mixture",
"subsets": ["sharegpt", "firefly", "codefuse", "metamathqa"],
"tags": ["chat", "sft", "general", "🔥"],
"huge_dataset": true
},
{
"ms_dataset_id": "ZhipuAI/LongWriter-6k",
"tags": ["long", "chat", "sft", "🔥"],
"hf_dataset_id": "zai-org/LongWriter-6k"
},
{
"ms_dataset_id": "swift/longwriter-6k-filtered",
"tags": ["long", "chat", "sft", "🔥"]
},
{
"ms_dataset_id": "AI-ModelScope/Magpie-Qwen2-Pro-300K-Filtered",
"tags": ["chat", "sft", "🔥"],
"hf_dataset_id": "Magpie-Align/Magpie-Qwen2-Pro-300K-Filtered"
},
{
"ms_dataset_id": "AI-ModelScope/Magpie-Qwen2-Pro-200K-Chinese",
"tags": ["chat", "sft", "🔥", "zh"],
"hf_dataset_id": "Magpie-Align/Magpie-Qwen2-Pro-200K-Chinese"
},
{
"ms_dataset_id": "AI-ModelScope/Magpie-Qwen2-Pro-200K-English",
"tags": ["chat", "sft", "🔥", "en"],
"hf_dataset_id": "Magpie-Align/Magpie-Qwen2-Pro-200K-English"
},
{
"ms_dataset_id": "PowerInfer/QWQ-LONGCOT-500K",
"tags": ["chat", "sft", "🔥", "cot"],
"hf_dataset_id": "PowerInfer/QWQ-LONGCOT-500K"
},
{
"ms_dataset_id": "PowerInfer/LONGCOT-Refine-500K",
"tags": ["chat", "sft", "🔥", "cot"],
"hf_dataset_id": "PowerInfer/LONGCOT-Refine-500K"
},
{
"ms_dataset_id": "ServiceNow-AI/R1-Distill-SFT",
"hf_dataset_id": "ServiceNow-AI/R1-Distill-SFT",
"tags": ["chat", "sft", "cot", "r1"],
"subsets": [{
"subset": "v0",
"columns": {
"problem": "query",
"reannotated_assistant_content": "response"
}
},
{
"subset": "v1",
"columns": {
"messages": "_",
"reannotated_messages": "messages"
}
}]
},
{
"ms_dataset_id": "bespokelabs/Bespoke-Stratos-17k",
"hf_dataset_id": "bespokelabs/Bespoke-Stratos-17k",
"tags": ["chat", "sft", "cot", "r1"]
},
{
"ms_dataset_id": "open-thoughts/OpenThoughts-114k",
"hf_dataset_id": "open-thoughts/OpenThoughts-114k",
"tags": ["chat", "sft", "cot", "r1"]
},
{
"ms_dataset_id": "HumanLLMs/Human-Like-DPO-Dataset",
"hf_dataset_id": "HumanLLMs/Human-Like-DPO-Dataset",
"columns": {
"prompt": "query",
"chosen": "response",
"rejected": "rejected_response"
},
"tags": ["rlhf", "dpo"]
},
{
"ms_dataset_id": "AI-ModelScope/MATH-lighteval",
"hf_dataset_id": "DigitalLearningGmbH/MATH-lighteval",
"columns": {
"problem": "query"
},
"tags": ["grpo", "math"]
},
{
"ms_dataset_id": "liucong/Chinese-DeepSeek-R1-Distill-data-110k-SFT",
"hf_dataset_id": "Congliu/Chinese-DeepSeek-R1-Distill-data-110k-SFT",
"tags": ["chat", "sft", "cot", "r1", "🔥"]
},
{
"ms_dataset_id": "AI-MO/NuminaMath-CoT",
"hf_dataset_id": "AI-MO/NuminaMath-CoT",
"tags": ["grpo", "math"]
},
{
"ms_dataset_id": "AI-MO/NuminaMath-TIR",
"hf_dataset_id": "AI-MO/NuminaMath-TIR",
"tags": ["grpo", "math", "🔥"]
},
{
"ms_dataset_id": "AI-MO/NuminaMath-1.5",
"hf_dataset_id": "AI-MO/NuminaMath-1.5",
"tags": ["grpo", "math"]
},
{
"ms_dataset_id": "FreedomIntelligence/medical-o1-reasoning-SFT",
"hf_dataset_id": "FreedomIntelligence/medical-o1-reasoning-SFT",
"subsets": ["en", "zh"],
"tags": ["medical", "o1", "🔥"]
},
{
"ms_dataset_id": "lmms-lab/multimodal-open-r1-8k-verified",
"hf_dataset_id": "lmms-lab/multimodal-open-r1-8k-verified",
"tags": ["grpo", "vision", "🔥"]
},
{
"ms_dataset_id": "open-r1/verifiable-coding-problems-python-10k",
"hf_dataset_id": "open-r1/verifiable-coding-problems-python-10k",
"tags": ["grpo", "code"]
},
{
"ms_dataset_id": "open-r1/verifiable-coding-problems-python",
"hf_dataset_id": "open-r1/verifiable-coding-problems-python",
"columns": {
"problem_statement": "query"
},
"tags": ["grpo", "code"]
},
{
"ms_dataset_id": "open-r1/verifiable-coding-problems-python-10k_decontaminated",
"hf_dataset_id": "open-r1/verifiable-coding-problems-python-10k_decontaminated",
"tags": ["grpo", "code"]
},
{
"ms_dataset_id": "open-r1/verifiable-coding-problems-python_decontaminated",
"hf_dataset_id": "open-r1/verifiable-coding-problems-python_decontaminated",
"columns": {
"problem_statement": "query"
},
"tags": ["grpo", "code"]
},
{
"ms_dataset_id": "iic/DocQA-RL-1.6K",
"hf_dataset_id": "Tongyi-Zhiwen/DocQA-RL-1.6K",
"columns": {
"prompt": "messages"
},
"tags": ["docqa", "rl", "long-sequence"]
},
{
"ms_dataset_id": "swift/Chinese-Qwen3-235B-2507-Distill-data-110k-SFT",
"tags": ["🔥", "distill", "sft"]
},
{
"ms_dataset_id": "swift/Chinese-Qwen3-235B-Thinking-2507-Distill-data-110k-SFT",
"tags": ["🔥", "distill", "sft", "cot", "r1", "thinking"]
}
]
+2
View File
@@ -0,0 +1,2 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
from . import llm, mllm
+959
View File
@@ -0,0 +1,959 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import ast
import json
import numpy as np
import re
from functools import partial
from typing import Any, Dict, List, Optional, Tuple, Union
from swift.template import split_str_parts_by
from ..preprocessor import (AlpacaPreprocessor, ClsGenerationPreprocessor, ClsPreprocessor, MessagesPreprocessor,
ResponsePreprocessor, RowPreprocessor, TextGenerationPreprocessor)
from ..register import DatasetMeta, SubsetDataset, register_dataset
class AlpacaZhPreprocessor(AlpacaPreprocessor):
@classmethod
def concat_inst_input(cls, instruction, input_):
if input_ and input_.startswith('输入:'):
input_ = input_[3:]
return super().concat_inst_input(instruction, input_)
register_dataset(
DatasetMeta(
ms_dataset_id='AI-ModelScope/alpaca-gpt4-data-zh',
hf_dataset_id='llm-wizard/alpaca-gpt4-data-zh',
preprocess_func=AlpacaZhPreprocessor(),
tags=['chat', 'general', '🔥'],
))
class LongAlpacaPreprocessor(AlpacaPreprocessor):
def preprocess(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
response = row['response']
prefix_prompt = 'Answer: '
if response and response.startswith(prefix_prompt):
response = response[len(prefix_prompt):].strip()
row['output'] = response
return super().preprocess(row)
register_dataset(
DatasetMeta(
ms_dataset_id='AI-ModelScope/LongAlpaca-12k',
hf_dataset_id='Yukang/LongAlpaca-12k',
preprocess_func=LongAlpacaPreprocessor(),
tags=['long-sequence', 'QA'],
))
class RuozhibaPreprocessor(RowPreprocessor):
def preprocess(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
title = row['title'] if row.get('title', None) is not None else row['content']
abs = row['abs'] if 'abs' in row else None
if abs and abs != title:
title = title + '' + abs
pattern = r'\d+[\.,\s,\、](.+)'
match = re.search(pattern, title)
if match:
title = match.group(1)
if title:
return {'messages': [{'role': 'assistant', 'content': title}]}
register_dataset(
DatasetMeta(
ms_dataset_id='AI-ModelScope/ruozhiba',
subsets=['post-annual', 'title-good', 'title-norm'],
preprocess_func=RuozhibaPreprocessor(),
tags=['pretrain', '🔥']))
class MathTrnPreprocessor(ResponsePreprocessor):
def preprocess(self, row):
query = row['query']
output = row['response']
row = {
'query': query,
'response': output,
}
return super().preprocess(row)
register_dataset(
DatasetMeta(ms_dataset_id='AI-ModelScope/math-trn-format', preprocess_func=MathTrnPreprocessor(), tags=['math']))
def _repair_ms_bench(messages: str) -> Optional[List[Dict[str, str]]]:
if isinstance(messages, str):
messages = ast.literal_eval(messages)
default_system = 'You are a helpful assistant.'
messages: List[Dict[str, str]]
if messages[0]['from'] == 'system' and messages[0]['value'] == default_system:
messages.pop(0)
# skip MOSS
for c in messages:
value = c['value'].lower()
if 'moss' in value or 'human:' in value or 'assistant:' in value or 'user:' in value:
return
return messages
register_dataset(
DatasetMeta(
ms_dataset_id='iic/ms_bench',
preprocess_func=MessagesPreprocessor(repair_messages=_repair_ms_bench),
tags=['chat', 'general', 'multi-round', '🔥']))
def _repair_agent_messages(messages: List[Dict[str, str]], use_mini: bool) -> Optional[List[Dict[str, str]]]:
if use_mini:
pattern = r'\d\. {"plugin_name": "(.+?)"'
if messages[0]['from'] != 'system':
return
system = messages[0]['value']
find_list = re.findall(pattern, system)
if len(set(find_list)) <= 1:
return
return messages
register_dataset(
DatasetMeta(
ms_dataset_id='damo/MSAgent-Bench',
subsets=[
SubsetDataset(
preprocess_func=MessagesPreprocessor(repair_messages=partial(_repair_agent_messages, use_mini=False))),
SubsetDataset(
name='mini',
preprocess_func=MessagesPreprocessor(repair_messages=partial(_repair_agent_messages, use_mini=True)),
is_weak_subset=True)
],
split=['train', 'validation'],
tags=['chat', 'agent', 'multi-round']))
advertise_gen_prompt = """Task: Generating advertisements based on keywords.
Keywords: {{QUERY}}
Advertisements:"""
register_dataset(
DatasetMeta(
ms_dataset_id='lvjianjin/AdvertiseGen',
hf_dataset_id='shibing624/AdvertiseGen',
preprocess_func=TextGenerationPreprocessor(
prompt=advertise_gen_prompt, columns={
'content': 'query',
'summary': 'response'
}),
tags=['text-generation', '🔥'],
split=['train', 'validation'],
))
class FireflyPreprocessor(ResponsePreprocessor):
_firefly_kind_list = {
'ProseGeneration', 'MRC', 'JinYongGeneration', 'TextCorrection', 'ClassicalChinese', 'BELLE', 'StoryGeneration',
'Couplet', 'Cot', 'Dictionary', 'Translation', 'Program', 'SentimentAnalyze', 'OpenQA', 'AncientPoem',
'TextMatching', 'NLI', 'Summary', 'KeywordRecognition', 'ProductDesc', 'LyricGeneration', 'Composition',
'MusicComment', 'NER'
}
def preprocess(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
if row['kind'] not in FireflyPreprocessor._firefly_kind_list:
return
return super().preprocess(row)
register_dataset(
DatasetMeta(
ms_dataset_id='AI-ModelScope/firefly-train-1.1M',
hf_dataset_id='YeungNLP/firefly-train-1.1M',
preprocess_func=FireflyPreprocessor(),
tags=['chat', 'general'],
))
register_dataset(
DatasetMeta(
ms_dataset_id='modelscope/clue',
hf_dataset_id='clue',
subsets=['cmnli'],
preprocess_func=ClsGenerationPreprocessor(['neutral', 'entailment', 'contradiction'],
task='Natural Language Inference',
is_pair_seq=True),
tags=['text-generation', 'classification'],
split=['train', 'validation'],
))
register_dataset(
DatasetMeta(
ms_dataset_id='DAMO_NLP/jd',
subsets=[
SubsetDataset(
'default',
'default',
preprocess_func=ClsGenerationPreprocessor(['negative', 'positive'],
task='Sentiment Classification',
is_pair_seq=False)),
SubsetDataset(
'cls',
'default',
preprocess_func=ClsPreprocessor(columns={'sentence': 'query'}),
),
],
tags=['text-generation', 'classification', '🔥'],
split=['train', 'validation'],
))
class SyntheticText2SqlPreprocessor(ResponsePreprocessor):
def preprocess(self, row: Dict[str, Any]) -> Dict[str, Any]:
sql_prompt = row['sql_prompt']
sql_context = row['sql_context']
sql = row['sql']
sql_explanation = row['sql_explanation']
query = f'Sql Table information:\n{sql_context}\n{sql_prompt}'
response = f'Let\'s think step by step:\n{sql_explanation}\nSo the final sql is:\n{sql}'
return super().preprocess({'query': query, 'response': response})
register_dataset(
DatasetMeta(
ms_dataset_id='AI-ModelScope/synthetic_text_to_sql',
hf_dataset_id='gretelai/synthetic_text_to_sql',
preprocess_func=SyntheticText2SqlPreprocessor(),
tags=['nl2sql', 'en']))
def _repair_toolbench(conversations: List[Dict[str, str]]) -> List[Dict[str, str]]:
assert len(conversations) == 2
if conversations[1]['from'] in {'caller', 'conclusion'}:
conversations[1]['from'] = 'assistant'
return conversations
register_dataset(
DatasetMeta(
ms_dataset_id='shenweizhou/alpha-umi-toolbench-processed-v2',
subsets=['backbone', 'caller', 'planner', 'summarizer'],
preprocess_func=MessagesPreprocessor(repair_messages=_repair_toolbench),
tags=['chat', 'agent', '🔥'],
huge_dataset=True))
class BlossomMathPreprocessor(ResponsePreprocessor):
def preprocess(self, row: Dict[str, Any]) -> Dict[str, Any]:
output, answer = row['output'], row['answer']
return super().preprocess({'query': row['query'], 'response': f'{output}\n\nAnswer: {answer}'})
register_dataset(
DatasetMeta(
ms_dataset_id='AI-ModelScope/blossom-math-v2',
hf_dataset_id='Azure99/blossom-math-v2',
preprocess_func=BlossomMathPreprocessor(),
tags=['chat', 'math', '🔥']))
register_dataset(
DatasetMeta(
ms_dataset_id='AI-ModelScope/sql-create-context',
hf_dataset_id='b-mc2/sql-create-context',
preprocess_func=AlpacaPreprocessor(columns={
'question': 'instruction',
'context': 'input',
'answer': 'output'
}),
tags=['chat', 'sql', '🔥']))
class TigerBotLawPreprocessor(ResponsePreprocessor):
def preprocess(self, row: Dict[str, Any]) -> Dict[str, Any]:
prompt = """{type}
{title}
"""
cur_prompt = prompt.format(type=row['type'], title=row['title'])
for i in range(1, 4):
chapter = row[f'chapter{i}']
if chapter is not None:
cur_prompt += f'{chapter}'
cur_prompt += f'{row["response"]}'
return super().preprocess({'response': cur_prompt})
register_dataset(
DatasetMeta(
ms_dataset_id='AI-ModelScope/tigerbot-law-plugin',
hf_dataset_id='TigerResearch/tigerbot-law-plugin',
preprocess_func=TigerBotLawPreprocessor(),
tags=['text-generation', 'law', 'pretrained']))
register_dataset(
DatasetMeta(
ms_dataset_id='codefuse-ai/CodeExercise-Python-27k',
preprocess_func=MessagesPreprocessor(columns={'chat_rounds': 'messages'}),
tags=['chat', 'coding', '🔥']))
class LeetcodePythonPreprocessor(ResponsePreprocessor):
def preprocess(self, row: Dict[str, Any]) -> Dict[str, Any]:
code_with_problem = row['code_with_problem']
idx = code_with_problem.find('```python')
problem = code_with_problem[:idx]
if problem.startswith('# '):
problem = problem[2:]
code = code_with_problem[idx:].strip()
explanation = row['explanation_only']
return super().preprocess({'query': problem, 'response': f'{code}\n\n{explanation}'})
register_dataset(
DatasetMeta(
ms_dataset_id='AI-ModelScope/leetcode-solutions-python',
preprocess_func=LeetcodePythonPreprocessor(),
tags=['chat', 'coding', '🔥']))
class StsbPreprocessor(RowPreprocessor):
def __init__(self, sim_threshold: Optional[float] = None):
self.sim_threshold = sim_threshold
super().__init__()
def preprocess(self, row: Dict[str, Any]) -> Dict[str, Any]:
row = {
'messages': [{
'role': 'user',
'content': row['sentence1']
}],
'positive_messages': [[{
'role': 'user',
'content': row['sentence2']
}]],
'label': row['score'],
}
if self.sim_threshold is None or float(row['label']) >= self.sim_threshold:
return row
else:
return None
class StsbGeneratePreprocessor(ResponsePreprocessor):
prompt = """Task: Based on the given two sentences, provide a similarity score between 0.0 and 1.0.
Sentence 1: {text1}
Sentence 2: {text2}
Similarity score: """
def preprocess(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
return super().preprocess({
'query': self.prompt.format(text1=row['sentence1'], text2=row['sentence2']),
'response': f"{row['score']:.1f}"
})
class StsbRegressionPreprocessor(StsbGeneratePreprocessor):
def preprocess(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
return super(StsbGeneratePreprocessor, self).preprocess({
'query':
self.prompt.format(text1=row['sentence1'], text2=row['sentence2']),
'label':
row['score']
})
register_dataset(
DatasetMeta(
ms_dataset_id='sentence-transformers/stsb',
hf_dataset_id='sentence-transformers/stsb',
subsets=[
SubsetDataset('default', preprocess_func=StsbPreprocessor()), # embedding
SubsetDataset('positive', preprocess_func=StsbPreprocessor(sim_threshold=0.75)), # infonce
SubsetDataset('generate', preprocess_func=StsbGeneratePreprocessor()),
SubsetDataset('reg', preprocess_func=StsbRegressionPreprocessor()),
],
tags=['similarity', '🔥']))
class MTEBRerankPreprocessor(RowPreprocessor):
def preprocess(self, row: Dict[str, Any]) -> Dict[str, Any]:
query = row['query']
positives = row['positive'] if isinstance(row['positive'], list) else [row['positive']]
negatives = row['negative'] if isinstance(row['negative'], list) else [row['negative']]
messages = [{'role': 'user', 'content': query}]
positive_messages = [[{'role': 'assistant', 'content': positive}] for positive in positives]
negative_messages = [[{'role': 'assistant', 'content': negative}] for negative in negatives]
return {'messages': messages, 'positive_messages': positive_messages, 'negative_messages': negative_messages}
register_dataset(
DatasetMeta(
ms_dataset_id='MTEB/scidocs-reranking',
hf_dataset_id='mteb/scidocs-reranking',
split=['validation', 'test'],
preprocess_func=MTEBRerankPreprocessor(),
tags=['rerank', '🔥']))
register_dataset(
DatasetMeta(
ms_dataset_id='MTEB/stackoverflowdupquestions-reranking',
hf_dataset_id='mteb/stackoverflowdupquestions-reranking',
split=['train', 'test'],
preprocess_func=MTEBRerankPreprocessor(),
tags=['rerank', '🔥']))
def _repair_conversations_agent_instruct(s: str) -> List[Dict[str, Any]]:
s = s.replace('}\n {', '},\n {')
if isinstance(s, str):
s = ast.literal_eval(s)
return s
register_dataset(
DatasetMeta(
ms_dataset_id='huangjintao/AgentInstruct_copy',
subsets=['alfworld', 'db', 'kg', 'mind2web', 'os', 'webshop'],
preprocess_func=MessagesPreprocessor(repair_messages=_repair_conversations_agent_instruct),
tags=['chat', 'agent', 'multi-round']))
class MultiRoleAgentPreprocessor(RowPreprocessor):
def preprocess(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
conv = row['conversations']
res_prompt = '\n\n【注意事项】\n1. 这是聊天室,不要发送私信给任何人\n2. 仅代表你个人说话,不要扮演其他人,只根据对话历史进行回复\n3. 长话短说,不要说太多话,不要超过50字 '
history_prompt = '\n\n【chat history】'
conv_prompt = '\n {name}:{content}'
query, response = '', conv[-1]['value']
system = conv[0]['value'] if conv[0]['from'] == 'system' else ''
if conv[0]['from'] == 'user':
query = conv[0]['value']
elif 'next_speakers:' not in system:
if '【注意事项】' not in system and system:
system += res_prompt
system += history_prompt
system += ''.join([conv_prompt.format(name=c['from'], content=c['value']) for c in conv[1:-1]])
if not query or not response:
return
return {
'messages': [{
'role': 'system',
'content': system
}, {
'role': 'user',
'content': query
}, {
'role': 'assistant',
'content': response
}],
}
register_dataset(
DatasetMeta(
ms_dataset_id='iic/MSAgent-MultiRole',
preprocess_func=MultiRoleAgentPreprocessor(),
tags=['chat', 'agent', 'multi-round', 'role-play', 'multi-agent']))
register_dataset(DatasetMeta(ms_dataset_id='swift/ToolBench', tags=['chat', 'agent', 'multi-round']))
register_dataset(
DatasetMeta(
ms_dataset_id='tastelikefeet/competition_math',
subsets=[
SubsetDataset(
name='default',
subset='default',
split=['train', 'test'],
),
],
tags=['qa', 'math']))
register_dataset(DatasetMeta(ms_dataset_id='modelscope/gsm8k', subsets=['main'], split=['train'], tags=['qa', 'math']))
register_dataset(
DatasetMeta(ms_dataset_id='modelscope/MathR', subsets=['default', 'clean'], split=['train'], tags=['qa', 'math']))
register_dataset(
DatasetMeta(ms_dataset_id='modelscope/MathR-32B-Distill', subsets=['data'], split=['train'], tags=['qa', 'math']))
class CoundownTaskPreprocessor(ResponsePreprocessor):
def preprocess(self, row: Dict[str, Any]) -> Dict[str, Any]:
numbers = row['nums']
target = row.pop('response', None)
query = (f'Using the numbers {numbers}, create an equation that equals {target}.\n'
'You can use basic arithmetic operations (+, -, *, /) and each number can only be used once.\n'
'Show your work in <think> </think> tags. And return the final equation and answer '
'in <answer> </answer> tags, for example <answer> (1 + 2) / 3 * 4 = 4 </answer>.')
row.update({'target': target, 'query': query})
return super().preprocess(row)
register_dataset(
DatasetMeta(
ms_dataset_id='zouxuhong/Countdown-Tasks-3to4',
subsets=['default'],
preprocess_func=CoundownTaskPreprocessor(),
tags=['math']))
class HC3Preprocessor(ResponsePreprocessor):
prompt = """Classification Task: Are the following responses from a human or from ChatGPT?
Question: {question}
Answer: {answer}
Category: Human, ChatGPT
Output:"""
def preprocess(self, row):
rows = []
for response in ['Human', 'ChatGPT']:
query = self.prompt.format(
question=row['query'], answer=self.random_state.choice(row[f'{response.lower()}_answers']))
rows.append(super().preprocess({'query': query, 'response': response}))
return rows
class HC3ClsPreprocessor(HC3Preprocessor):
def preprocess(self, row):
rows = []
for i, response in enumerate(['Human', 'ChatGPT']):
query = self.prompt.format(
question=row['query'], answer=self.random_state.choice(row[f'{response.lower()}_answers']))
rows.append(ResponsePreprocessor.preprocess(self, {'query': query, 'label': i}))
return rows
hc3_subset_names = ['baike', 'open_qa', 'nlpcc_dbqa', 'finance', 'medicine', 'law', 'psychology']
hc3_subsets: List[SubsetDataset] = []
for hc3_subset_name in hc3_subset_names:
hc3_subsets.append(
SubsetDataset(
name=hc3_subset_name,
subset=hc3_subset_name,
preprocess_func=HC3Preprocessor(),
))
hc3_subsets.append(
SubsetDataset(
name=f'{hc3_subset_name}_cls',
subset=hc3_subset_name,
preprocess_func=HC3ClsPreprocessor(),
))
register_dataset(
DatasetMeta(
ms_dataset_id='simpleai/HC3-Chinese',
hf_dataset_id='Hello-SimpleAI/HC3-Chinese',
subsets=hc3_subsets,
tags=['text-generation', 'classification', '🔥']))
hc3_subset_names = ['finance', 'medicine']
hc3_subsets: List[SubsetDataset] = []
for hc3_subset_name in hc3_subset_names:
hc3_subsets.append(
SubsetDataset(
name=hc3_subset_name,
subset=hc3_subset_name,
preprocess_func=HC3Preprocessor(),
))
hc3_subsets.append(
SubsetDataset(
name=f'{hc3_subset_name}_cls',
subset=hc3_subset_name,
preprocess_func=HC3ClsPreprocessor(),
))
register_dataset(
DatasetMeta(
ms_dataset_id='simpleai/HC3',
hf_dataset_id='Hello-SimpleAI/HC3',
subsets=hc3_subsets,
preprocess_func=HC3Preprocessor(),
tags=['text-generation', 'classification', '🔥']))
class DureaderPreprocessor(RowPreprocessor):
def preprocess(self, row: Dict[str, Any]) -> Dict[str, Any]:
prompt = """Task: Question Generation
Context: {context}
Answer: {answer}
Question:"""
answer, context = row['text1'].split('[SEP]')
return {
'messages': [{
'role': 'user',
'content': prompt.format(context=context, answer=answer)
}, {
'role': 'assistant',
'content': row['text2']
}]
}
register_dataset(
DatasetMeta(
ms_dataset_id='modelscope/DuReader_robust-QG',
preprocess_func=DureaderPreprocessor(),
split=['train', 'validation', 'test'],
tags=['text-generation', '🔥']))
class HHRLHFPreprocessor(RowPreprocessor):
@staticmethod
def _to_messages(data):
messages = []
for query, response in zip(data[::2], data[1::2]):
messages.append({'role': 'user', 'content': query})
messages.append({'role': 'assistant', 'content': response})
return messages
def preprocess(self, row: Dict[str, Any]) -> Dict[str, Any]:
chosen = row['chosen'].strip()
rejected = row['rejected'].strip()
parts_chosen = [s.strip() for s in re.split('\n\nHuman:|\n\nAssistant:|\n\nHum:', chosen)]
parts_rejected = [s.strip() for s in re.split('\n\nHuman:|\n\nAssistant:|\n\nHum:', rejected)]
if parts_chosen[0].startswith('Human:'):
assert parts_rejected[0].startswith('Human:')
parts_chosen[0] = parts_chosen[0][6:].strip()
parts_rejected[0] = parts_rejected[0][6:].strip()
row['messages'] = self._to_messages(parts_chosen)
row['rejected_messages'] = self._to_messages(parts_rejected)
return row
# TODO meta file broken
register_dataset(
DatasetMeta(
ms_dataset_id='AI-ModelScope/hh-rlhf',
subsets=['helpful-base', 'helpful-online', 'helpful-rejection-sampled'],
preprocess_func=HHRLHFPreprocessor(),
split=['train', 'test'],
tags=['rlhf', 'dpo'],
huge_dataset=True))
class XlamFunctionCallingPreprocessor(RowPreprocessor):
def preprocess(self, row: Dict[str, Any]) -> Dict[str, Any]:
messages = [{'role': 'user', 'content': row['query']}]
response = row['answers']
response = json.loads(response)
messages += [{'role': 'tool_call', 'content': json.dumps(content)} for content in response]
return {'messages': messages, 'tools': row['tools']}
class XlamFunctionCallingGRPOPreprocessor(ResponsePreprocessor):
def preprocess(self, row: Dict[str, Any]) -> Dict[str, Any]:
query = row['query']
answers = row['response']
if isinstance(answers, str):
answers = json.loads(answers)
answer = np.random.choice(answers)
name = answer['name']
args = json.dumps(answer['arguments'])
response = f'Action: {name}\nAction Input: {args}'
row = {'query': query, 'response': response, 'solution': response, 'tools': row['tools']}
return super().preprocess(row)
register_dataset(
DatasetMeta(
ms_dataset_id='LLM-Research/xlam-function-calling-60k',
hf_dataset_id='Salesforce/xlam-function-calling-60k',
subsets=[
SubsetDataset('default', 'dataset', preprocess_func=XlamFunctionCallingPreprocessor()),
SubsetDataset('grpo', 'dataset', preprocess_func=XlamFunctionCallingGRPOPreprocessor())
],
tags=['agent', 'grpo', '🔥']))
class HHRLHFCNPreprocessor(MessagesPreprocessor):
def preprocess(self, row: Dict[str, Any]) -> Dict[str, Any]:
row['messages'].append(row.pop('chosen'))
row['rejected_response'] = row['rejected']['text']
return super().preprocess(row)
register_dataset(
DatasetMeta(
ms_dataset_id='AI-ModelScope/hh_rlhf_cn',
subsets=['hh_rlhf', 'harmless_base_cn', 'harmless_base_en', 'helpful_base_cn', 'helpful_base_en'],
preprocess_func=HHRLHFCNPreprocessor(columns={'context': 'messages'}, content_key='text'),
split=['train', 'test'],
tags=['rlhf', 'dpo', '🔥']))
def repair_conversations(s: Union[str, Any]) -> Any:
if isinstance(s, str):
s = s.replace('}\n {', '},{')
s = s.replace('}\n{', '},{')
s = s.replace('}{', '},{')
s = s.replace('}\n {', '},{')
return ast.literal_eval(s)
return s
register_dataset(
DatasetMeta(
ms_dataset_id='AI-ModelScope/lmsys-chat-1m',
hf_dataset_id='lmsys/lmsys-chat-1m',
preprocess_func=MessagesPreprocessor(repair_messages=repair_conversations),
tags=['chat', 'em']))
class EmojiPreprocessr(ResponsePreprocessor):
def preprocess(self, row: Dict[str, Any]) -> Dict[str, Any]:
# Remove dirty characters
row['query'] = row['query'].replace('', '')
row['response'] = row['response'].replace('', '')
row['rejected_response'] = row['rejected_response'].replace('', '')
return super().preprocess(row)
register_dataset(
DatasetMeta(
ms_dataset_id='hjh0119/shareAI-Llama3-DPO-zh-en-emoji',
hf_dataset_id='shareAI/DPO-zh-en-emoji',
preprocess_func=EmojiPreprocessr(columns={
'answer_zh': 'response',
'answer_en': 'rejected_response'
}),
tags=['rlhf', 'dpo']))
register_dataset(
DatasetMeta(ms_dataset_id='AI-ModelScope/ultrafeedback-binarized-preferences-cleaned-kto', tags=['rlhf', 'kto']))
register_dataset(
DatasetMeta(
ms_dataset_id='OmniData/Zhihu-KOL-More-Than-100-Upvotes',
hf_dataset_id='bzb2023/Zhihu-KOL-More-Than-100-Upvotes',
tags=['zhihu', 'qa']))
register_dataset(
DatasetMeta(
ms_dataset_id='OmniData/Zhihu-KOL',
hf_dataset_id='wangrui6/Zhihu-KOL',
huge_dataset=True,
tags=['zhihu', 'qa'],
))
class GuanacoPreprocessor(RowPreprocessor):
def preprocess(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
instruction = row['instruction']
input = row['input']
output = row['output']
history = []
if instruction:
parts = split_str_parts_by(
instruction, ['User:', 'User', 'Assistant', 'Assistant:', 'Asssistent:', 'Assistent:', 'Assistenz:'])
for idx, part in enumerate(parts):
if idx % 2 == 0:
if 'user' not in part['key'].lower():
return
history.append([part['content'], None])
else:
if 'assist' not in part['key'].lower() and 'asssist' not in part['key'].lower():
return
history[-1][-1] = part['content']
if input.startswith('User:'):
input = input[len('User:'):].strip()
if any([not h[0] or not h[1] for h in history]):
return
messages = []
for h in history:
messages.append({'role': 'user', 'content': h[0]})
messages.append({'role': 'assistant', 'content': h[1]})
messages.append({'role': 'user', 'content': input})
messages.append({'role': 'assistant', 'content': output})
return {
'messages': messages,
}
register_dataset(
DatasetMeta(
ms_dataset_id='AI-ModelScope/GuanacoDataset',
hf_dataset_id='JosephusCheung/GuanacoDataset',
preprocess_func=GuanacoPreprocessor(),
tags=['chat', 'zh']))
class FunctionCallChatmlPreprocessor(MessagesPreprocessor):
def preprocess(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
res = super().preprocess(row)
if res['function_description']:
res['tools'] = res['function_description'].split('\n\n')
messages = res['messages']
if messages[0]['role'] == 'system':
messages.pop(0)
return res
register_dataset(
DatasetMeta(
ms_dataset_id='AI-ModelScope/function-calling-chatml',
hf_dataset_id='Locutusque/function-calling-chatml',
preprocess_func=FunctionCallChatmlPreprocessor(),
tags=['agent', 'en', 'sft', '🔥']))
class Dolly15kPreprocessor(RowPreprocessor):
def preprocess(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
instruction = row['instruction']
context = row['context']
response = row['response']
query = ''
if context:
query = 'Here gives some useful information:\n'
query += context
query += '\n'
query += instruction
return {
'messages': [{
'role': 'user',
'content': query
}, {
'role': 'assistant',
'content': response
}],
}
register_dataset(
DatasetMeta(
ms_dataset_id='AI-ModelScope/databricks-dolly-15k',
hf_dataset_id='databricks/databricks-dolly-15k',
preprocess_func=Dolly15kPreprocessor(),
tags=['multi-task', 'en', 'quality']))
class OrpoDPOMix40kPreprocessor(MessagesPreprocessor):
def preprocess(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
if row['source'] == 'toxic-dpo-v0.2':
return
return super().preprocess(row)
register_dataset(
DatasetMeta(
ms_dataset_id='AI-ModelScope/orpo-dpo-mix-40k',
hf_dataset_id='mlabonne/orpo-dpo-mix-40k',
preprocess_func=OrpoDPOMix40kPreprocessor(columns={
'chosen': 'messages',
'rejected': 'rejected_messages'
}),
tags=['dpo', 'orpo', 'en', 'quality']))
register_dataset(
DatasetMeta(
ms_dataset_id='swift/sharegpt',
subsets=['common-zh', 'unknow-zh', 'common-en'],
tags=['chat', 'general', 'multi-round']))
class SelfCognitionPreprocessor(ResponsePreprocessor):
def __init__(self, *args, query_suffix: str = '', response_prefix: str = '', **kwargs):
self.query_suffix = query_suffix
self.response_prefix = response_prefix
self.name: Optional[Tuple[str, str]] = None
self.author: Optional[Tuple[str, str]] = None
super().__init__(*args, **kwargs)
def set_name_author(self, name, author):
self.name = name
self.author = author
def preprocess(self, row: Dict[str, Any]) -> Dict[str, Any]:
for key in ['name', 'author']:
val = getattr(self, key)
if val is None:
continue
val = val[0] if row['tag'] == 'zh' else val[1]
if val is None:
continue
placeholder = '{{' + key.upper() + '}}'
row['query'] = row['query'].replace(placeholder, val)
row['response'] = row['response'].replace(placeholder, val)
row['query'] = row['query'] + self.query_suffix
row['response'] = self.response_prefix + row['response']
return super().preprocess(row)
register_dataset(
DatasetMeta(
ms_dataset_id='swift/self-cognition',
hf_dataset_id='modelscope/self-cognition',
subsets=[
SubsetDataset(preprocess_func=SelfCognitionPreprocessor()),
SubsetDataset(
'qwen3',
preprocess_func=SelfCognitionPreprocessor(
query_suffix=' /no_think', response_prefix='<think>\n\n</think>\n\n')),
SubsetDataset(
'empty_think', preprocess_func=SelfCognitionPreprocessor(response_prefix='<think>\n\n</think>\n\n')),
],
dataset_name='self-cognition',
tags=['chat', 'self-cognition', '🔥']))
register_dataset(
DatasetMeta(
ms_dataset_id='open-r1/DAPO-Math-17k-Processed',
hf_dataset_id='open-r1/DAPO-Math-17k-Processed',
subsets=['all'],
tags=['math', 'rlvr']))
class SudokuPreprocessor(ResponsePreprocessor):
prompt = ('Solve the following 9x9 Sudoku puzzle. '
"Empty cells are marked with '0'. "
'Provide the completed grid as your answer.\n\n'
'Puzzle:\n{puzzle}')
@staticmethod
def _format_grid(s: str) -> str:
return '\n'.join(s[i:i + 9] for i in range(0, len(s), 9))
def preprocess(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
puzzle = row['query'].replace('.', '0')
response = row['response']
puzzle = self._format_grid(puzzle)
response = self._format_grid(response)
return super().preprocess({'query': self.prompt.format(puzzle=puzzle), 'response': response})
register_dataset(
DatasetMeta(
ms_dataset_id='sapientinc/sudoku-extreme-1k',
hf_dataset_id='sapientinc/sudoku-extreme-1k',
preprocess_func=SudokuPreprocessor(),
))
File diff suppressed because it is too large Load Diff
+202
View File
@@ -0,0 +1,202 @@
# 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] = {}
+127
View File
@@ -0,0 +1,127 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import os
import platform
import re
from dataclasses import dataclass, field
from typing import Dict, List, Literal, Optional, Tuple
from .dataset_meta import DATASET_MAPPING, DatasetMeta
_dataset_meta_mapping = None
@dataclass
class DatasetSyntax:
dataset: str
subsets: List[str] = field(default_factory=list)
dataset_sample: Optional[int] = None
use_hf: Optional[bool] = None
def __post_init__(self):
if os.path.isfile(self.dataset):
self.dataset_type = 'path'
else: # dataset_id or dataset_dir
self.dataset_type = 'repo'
def get_raw(self):
subsets = '/'.join(self.subsets)
dataset_sample = '' if self.dataset_sample is None else f'#{self.dataset_sample}'
return f'{self.dataset}{subsets}{dataset_sample}'
@staticmethod
def _safe_split(s: str,
sep: str,
use_0: bool,
split_mode: Literal['left', 'right'] = 'left') -> Tuple[Optional[str], Optional[str]]:
"""
use_0: When the length of the part is 1, is it considered as part0 or part1.
split_mode: use split or rsplit
"""
if s is None or len(s) == 0:
return None, None
if split_mode == 'left':
part = s.split(sep, 1)
else:
part = s.rsplit(sep, 1)
if len(part) == 1:
if use_0:
part = part[0], None
else:
part = None, part[0]
else:
assert len(part) == 2
return part
@classmethod
def parse(cls, dataset: str) -> 'DatasetSyntax':
"""Parse the dataset from the command line"""
# hf/ms::dataset_id or dataset_path:subset1/subset2/subset3#dataset_sample
if os.path.exists(dataset):
use_hf = None
else:
use_hf, dataset = cls._safe_split(dataset, '::', False)
if isinstance(use_hf, str):
use_hf = use_hf.lower()
use_hf = {'hf': True, 'ms': False}.get(use_hf)
if os.path.exists(dataset):
other, dataset_sample = dataset, None
else:
other, dataset_sample = cls._safe_split(dataset, '#', True, 'right')
if os.path.exists(other):
dataset, subsets = other, None
else:
dataset, subsets = cls._safe_split(other, ':', True)
if subsets is not None:
subsets = [subset.strip() for subset in subsets.split('/')]
if dataset_sample is not None:
dataset_sample = int(dataset_sample)
return cls(dataset.strip(), subsets or [], dataset_sample, use_hf)
def get_dataset_meta(self, use_hf: bool):
dataset_meta_mapping = self._get_dataset_meta_mapping()
dataset_type = self.dataset_type
if dataset_type == 'path':
dataset_meta = dataset_meta_mapping.get((dataset_type, self.dataset))
else:
dataset_type = 'repo' if os.path.isdir(self.dataset) else {True: 'hf', False: 'ms'}[use_hf]
dataset_meta = dataset_meta_mapping.get((dataset_type, self.dataset))
return dataset_meta or self._get_matched_dataset_meta(dataset_meta_mapping) or DatasetMeta()
@staticmethod
def _get_dataset_meta_mapping() -> Dict[Tuple[str, str], DatasetMeta]:
global _dataset_meta_mapping
if _dataset_meta_mapping is not None:
return _dataset_meta_mapping
_dataset_meta_mapping = {}
for dataset_meta in DATASET_MAPPING.values():
if dataset_meta.dataset_path is not None:
dataset_type = 'repo' if os.path.isdir(dataset_meta.dataset_path) else 'path'
_dataset_meta_mapping[(dataset_type, dataset_meta.dataset_path)] = dataset_meta
if dataset_meta.ms_dataset_id is not None:
_dataset_meta_mapping[('ms', dataset_meta.ms_dataset_id)] = dataset_meta
if dataset_meta.hf_dataset_id is not None:
_dataset_meta_mapping[('hf', dataset_meta.hf_dataset_id)] = dataset_meta
return _dataset_meta_mapping
@staticmethod
def get_dataset_name(dataset_id: str) -> str:
# compat hf hub
dataset_id = dataset_id.rstrip('/')
match_ = re.search('/datasets--.+?--(.+?)/snapshots/', dataset_id)
if match_ is not None:
return match_.group(1)
dataset_name = dataset_id.rsplit('/', 1)[-1]
if platform.system().lower() == 'windows':
dataset_name = dataset_name.rsplit('\\', 1)[-1]
return dataset_name
def _get_matched_dataset_meta(self, dataset_meta_mapping):
suffix_dataset_meta_mapping = {}
for dataset_name, dataset_meta in dataset_meta_mapping.items():
dataset_name = self.get_dataset_name(dataset_name[1])
suffix_dataset_meta_mapping[dataset_name] = dataset_meta
dataset_name = self.get_dataset_name(self.dataset)
dataset_meta = suffix_dataset_meta_mapping.get(dataset_name)
return dataset_meta
+132
View File
@@ -0,0 +1,132 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import bisect
import mmap
import os
import pickle
import threading
from modelscope.hub.utils.utils import get_cache_dir
from queue import Queue
from torch.utils.data import Dataset
from typing import Any, List
class IndexedDatasetBuilder:
CHUNK_SIZE = 1e10
def __init__(self, dataset_name: str):
self.cache_dir = IndexedDataset.get_cache_dir(dataset_name)
self.n_shard = 1
self.bin_path = os.path.join(self.cache_dir, IndexedDataset.BIN_FNAME.format(0))
self.idx_path = os.path.join(self.cache_dir, IndexedDataset.IDX_FNAME)
if os.path.exists(self.bin_path):
os.remove(self.bin_path)
self.bin_file = open(self.bin_path, 'ab')
self.length_list = []
self.idx_list = [0]
self.shard_offset = [0]
self._thread = None
self._queue = Queue(maxsize=1000)
def _write_worker(self):
while True:
items = self._queue.get()
if items is None:
break
bin_buffer = []
for item in items:
item_buffer = pickle.dumps(item)
bin_buffer.append(item_buffer)
self.idx_list.append(self.idx_list[-1] + len(item_buffer))
self.length_list.append(item['length'])
self.bin_file.write(b''.join(bin_buffer))
offset = self.idx_list[-1] - self.shard_offset[-1]
if offset >= self.CHUNK_SIZE:
self.bin_file.close()
self.bin_path = os.path.join(self.cache_dir, IndexedDataset.BIN_FNAME.format(self.n_shard))
self.shard_offset.append(self.shard_offset[-1] + offset)
self.n_shard += 1
if os.path.exists(self.bin_path):
os.remove(self.bin_path)
self.bin_file = open(self.bin_path, 'ab')
def add_items(self, items: List[Any]) -> None:
if self._thread is None:
self._thread = threading.Thread(target=self._write_worker, daemon=True)
self._thread.start()
self._queue.put(items)
def finalize(self):
if self._thread is not None:
self._queue.put(None)
self._thread.join()
self.bin_file.close()
idx_obj = {
'idx': self.idx_list,
'length': self.length_list,
'n_shard': self.n_shard,
'shard_offset': self.shard_offset,
}
with open(self.idx_path, 'wb') as f:
pickle.dump(idx_obj, f)
class BinReader:
def __init__(self, bin_path: str):
self.bin_path = bin_path
self.file = open(bin_path, 'rb')
try:
self.mm = mmap.mmap(self.file.fileno(), 0, access=mmap.ACCESS_READ)
except ValueError:
# For example, self.file is an empty file.
self.mm = None
def read_buffer(self, offset: int, size: int) -> bytes:
if offset < 0 or size < 0 or offset + size > len(self.mm):
raise ValueError('Invalid offset or size')
return self.mm[offset:offset + size]
def __del__(self):
if self.mm is not None:
self.mm.close()
self.file.close()
class IndexedDataset(Dataset):
BIN_FNAME = 'data-{:05d}.bin'
IDX_FNAME = 'data.idx'
@staticmethod
def get_cache_dir(dataset_name: str):
cache_dir = os.getenv('PACKING_CACHE') or os.path.join(get_cache_dir(), 'tmp')
cache_dir = os.path.join(cache_dir, dataset_name)
os.makedirs(cache_dir, exist_ok=True)
assert dataset_name is not None, f'dataset_name: {dataset_name}'
return cache_dir
def __init__(self, dataset_name: str):
self.dataset_name = dataset_name
cache_dir = self.get_cache_dir(dataset_name)
self.idx_path = os.path.join(cache_dir, self.IDX_FNAME)
with open(self.idx_path, 'rb') as f:
idx_obj = pickle.load(f)
self.idx_list = idx_obj['idx']
self.length_list = idx_obj['length']
self.n_shard = idx_obj['n_shard']
self.shard_offset = idx_obj['shard_offset']
self.bin_readers = []
for i in range(self.n_shard):
bin_path = os.path.join(cache_dir, self.BIN_FNAME.format(i))
self.bin_readers.append(BinReader(bin_path))
def __getitem__(self, index: int):
if index < 0:
index = index % len(self)
idx, idx_next = self.idx_list[index], self.idx_list[index + 1]
num_shard = bisect.bisect_right(self.shard_offset, idx)
offset = self.shard_offset[num_shard - 1]
buffer = self.bin_readers[num_shard - 1].read_buffer(idx - offset, idx_next - idx)
return pickle.loads(buffer)
def __len__(self):
return len(self.idx_list) - 1
+384
View File
@@ -0,0 +1,384 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import numpy as np
import os
from contextlib import nullcontext
from datasets import Dataset as HfDataset
from datasets import IterableDataset as HfIterableDataset
from datasets import load_dataset as hf_load_dataset
from functools import partial
from modelscope.hub.utils.utils import get_cache_dir
from typing import Dict, List, Literal, Optional, Tuple, Union
from swift.hub import get_hub
from swift.utils import get_logger, get_seed, safe_ddp_context, use_hf_hub
from .dataset_meta import DATASET_TYPE, BaseDatasetLoader
from .dataset_syntax import DatasetSyntax
from .preprocessor import RowPreprocessor
from .register import DATASET_MAPPING, DatasetMeta, SubsetDataset
logger = get_logger()
class DatasetLoader(BaseDatasetLoader):
def __init__(
self,
num_proc: int = 1,
load_from_cache_file: bool = True,
streaming: bool = False,
hub_token: Optional[str] = None,
strict: bool = False,
download_mode: Literal['force_redownload', 'reuse_dataset_if_exists'] = 'reuse_dataset_if_exists',
columns: Optional[Dict[str, str]] = None,
remove_unused_columns: bool = True,
disable_auto_column_mapping: bool = False,
):
self.num_proc = num_proc
self.load_from_cache_file = load_from_cache_file
self.streaming = streaming
self.hub_token = hub_token
self.strict = strict
self.download_mode = download_mode
self.columns = columns
self.remove_unused_columns = remove_unused_columns
self.disable_auto_column_mapping = disable_auto_column_mapping
def _load_dataset_path(
self,
dataset_path: str,
dataset_meta: DatasetMeta,
) -> HfDataset:
ext = os.path.splitext(dataset_path)[1].lstrip('.')
file_type = {'jsonl': 'json', 'txt': 'text'}.get(ext) or ext
kwargs = {'split': 'train', 'streaming': self.streaming, 'num_proc': self.num_proc}
if file_type == 'csv':
kwargs['na_filter'] = False
with safe_ddp_context(None, True):
kwargs['cache_dir'] = os.path.join(get_cache_dir(), 'datasets')
dataset = hf_load_dataset(file_type, data_files=dataset_path, **kwargs)
if self.columns:
dataset = RowPreprocessor.safe_rename_columns(dataset, self.columns)
dataset = dataset_meta.preprocess_func(
dataset,
num_proc=self.num_proc,
load_from_cache_file=self.load_from_cache_file,
strict=self.strict,
enable_auto_mapping=not self.disable_auto_column_mapping)
if self.remove_unused_columns:
dataset = RowPreprocessor.remove_useless_columns(dataset)
return dataset
def _load_repo_dataset(
self,
dataset_id: str,
subset: SubsetDataset,
*,
use_hf: Optional[bool] = None,
revision: Optional[str] = None,
) -> HfDataset:
datasets = []
if os.path.isdir(dataset_id):
retry = 1
load_context = nullcontext
use_hf = True
dataset_str = f'Use local folder, dataset_dir: {dataset_id}'
# The dataset downloaded from modelscope will have an additional dataset_infos.json file.
with safe_ddp_context('dataset_infos_rename'):
dataset_infos_path = os.path.join(dataset_id, 'dataset_infos.json')
if os.path.isfile(dataset_infos_path):
os.rename(dataset_infos_path, f'{dataset_infos_path}_bak')
elif dataset_id.startswith('/'):
raise ValueError(f'The local path does not exist, dataset_id: `{dataset_id}`. '
f'os.path.exists(dataset_id): {os.path.exists(dataset_id)}')
else:
retry = 3
load_context = partial(safe_ddp_context, hash_id=dataset_id, use_barrier=True)
dataset_str_f = 'Downloading the dataset from {hub}, dataset_id: {dataset_id}'
if use_hf:
dataset_str = dataset_str_f.format(hub='HuggingFace', dataset_id=dataset_id)
else:
dataset_str = dataset_str_f.format(hub='ModelScope', dataset_id=dataset_id)
logger.info(dataset_str)
hub = get_hub(use_hf)
for split in subset.split:
i = 1
with load_context():
while True:
try:
dataset = hub.load_dataset(
dataset_id,
subset.subset,
split,
streaming=self.streaming,
revision=revision,
download_mode=self.download_mode,
hub_token=self.hub_token,
num_proc=self.num_proc)
except Exception as e:
if i == retry:
raise
i += 1
logger.error(f'Dataset {dataset_id} load failed: subset_name={subset.subset},'
f'split={split} with error: {e}')
else:
break
if hasattr(dataset, '_hf_ds'):
dataset = dataset._hf_ds
if self.streaming and isinstance(dataset, HfDataset):
dataset = dataset.to_iterable_dataset()
if self.columns:
dataset = RowPreprocessor.safe_rename_columns(dataset, self.columns)
dataset = subset.preprocess_func(
dataset,
num_proc=self.num_proc,
load_from_cache_file=self.load_from_cache_file,
strict=self.strict,
enable_auto_mapping=not self.disable_auto_column_mapping)
if self.remove_unused_columns:
dataset = RowPreprocessor.remove_useless_columns(dataset)
datasets.append(dataset)
return self.concat_datasets(datasets)
@staticmethod
def _select_subsets(subsets: List[str], dataset_meta: DatasetMeta) -> List[SubsetDataset]:
subset_mapping = {subset.name: subset for subset in dataset_meta.subsets}
subset_names = list(subset_mapping.keys())
if not subsets:
if len(subset_names) <= 1:
subsets = subset_names
elif 'default' in subset_names:
subsets = ['default']
else:
raise ValueError(f'Please provide subsets. available subsets: {subset_names}')
elif len(subsets) == 1 and subsets[0] == 'all' and 'all' not in subset_names:
subsets = [subset_name for subset_name in subset_names if not subset_mapping[subset_name].is_weak_subset]
subsets = [
subset_mapping[subset_name] if subset_name in subset_mapping else SubsetDataset(subset=subset_name)
for subset_name in subsets
]
return [subset.set_default(dataset_meta) for subset in subsets]
def load(
self,
dataset_syntax: Optional[DatasetSyntax] = None,
dataset_meta: Optional[DatasetMeta] = None,
*,
use_hf: Optional[bool] = None,
) -> HfDataset:
if dataset_syntax.dataset_type == 'path':
dataset = self._load_dataset_path(
dataset_syntax.dataset,
dataset_meta=dataset_meta,
)
else:
subsets: List[SubsetDataset] = self._select_subsets(dataset_syntax.subsets, dataset_meta)
revision = dataset_meta.hf_revision if use_hf else dataset_meta.ms_revision
datasets = []
for subset in subsets:
dataset = self._load_repo_dataset(
dataset_syntax.dataset,
subset,
use_hf=use_hf,
revision=revision,
)
datasets.append(dataset)
dataset = self.concat_datasets(datasets)
return dataset
def init_self_cognition_preprocessor(
dataset_meta: Optional[DatasetMeta],
model_name: Optional[Union[Tuple[str, str], List[str]]] = None,
model_author: Optional[Union[Tuple[str, str], List[str]]] = None,
) -> None:
from .dataset.llm import SelfCognitionPreprocessor
if dataset_meta is None or model_name is None and model_author is None:
return
kwargs = {}
# zh, en
for key in ['name', 'author']:
val = locals()[f'model_{key}']
if isinstance(val, str):
val = [val]
if val is not None and val[0] is not None and (len(val) == 1 or val[1] is None):
val = (val[0], val[0])
kwargs[key] = val
preprocess_funcs = [dataset_meta.preprocess_func]
preprocess_funcs += [subset.preprocess_func for subset in dataset_meta.subsets if isinstance(subset, SubsetDataset)]
for preprocess_func in preprocess_funcs:
if isinstance(preprocess_func, SelfCognitionPreprocessor):
preprocess_func.set_name_author(**kwargs)
logger.info_once(f"SelfCognitionPreprocessor has been successfully configured with name: {kwargs['name']}, "
f"author: {kwargs['author']}.")
def _inject_dataset_routing_tag(dataset: DATASET_TYPE, ds_name: str) -> DATASET_TYPE:
"""Inject ``dataset`` column for multi-teacher routing (constant per source dataset)."""
if isinstance(dataset, HfIterableDataset):
return dataset.map(lambda example: {**example, 'dataset': ds_name})
return dataset.add_column('dataset', [ds_name] * len(dataset))
def load_dataset(
datasets: Union[List[str], str],
*,
split_dataset_ratio: float = 0.,
seed: Union[int, np.random.RandomState, None] = 42,
num_proc: int = 1,
load_from_cache_file: bool = True,
shuffle: bool = False,
streaming: bool = False,
interleave_prob: Optional[List[float]] = None,
stopping_strategy: Literal['first_exhausted', 'all_exhausted'] = 'first_exhausted',
shuffle_buffer_size: int = 1000,
use_hf: Optional[bool] = None,
hub_token: Optional[str] = None,
strict: bool = False,
download_mode: Literal['force_redownload', 'reuse_dataset_if_exists'] = 'reuse_dataset_if_exists',
columns: Optional[Dict[str, str]] = None, # columns_mapping
remove_unused_columns: bool = True,
disable_auto_column_mapping: bool = False,
# self-cognition
model_name: Optional[Union[Tuple[str, str], List[str]]] = None, # zh, en
model_author: Optional[Union[Tuple[str, str], List[str]]] = None,
) -> Tuple[DATASET_TYPE, Optional[DATASET_TYPE]]:
"""Load and preprocess datasets.
This function provides a unified interface to load datasets from various sources (HuggingFace,
ModelScope, or local paths), with support for splitting, shuffling, streaming, and interleaving
multiple datasets. It also handles self-cognition dataset preprocessing for model training.
Args:
datasets: Single dataset name or list of dataset names to load. Can use special syntax
for advanced configurations (e.g., 'dataset_name#1000' for sampling).
split_dataset_ratio: Ratio for splitting dataset into train/validation sets.
Value between 0 and 1. If 0, no validation split is created. Default: 0.
seed: Random seed for reproducibility. Can be an integer or numpy RandomState object.
If None, results will be non-deterministic. Default: 42.
num_proc: Number of processes to use for dataset preprocessing. Set to None for
streaming mode. Default: 1.
load_from_cache_file: Whether to load preprocessed data from cache if available.
Default: True.
shuffle: Whether to shuffle the dataset(s) after loading. Default: False.
streaming: Enable streaming mode for large datasets that don't fit in memory.
When True, num_proc is automatically set to None. Default: False.
interleave_prob: Probability weights for interleaving multiple datasets. Must have
same length as datasets list. If None, datasets are concatenated instead. Default: None.
stopping_strategy: Strategy when interleaving datasets of different lengths:
- 'first_exhausted': Stop when shortest dataset is exhausted
- 'all_exhausted': Continue until all datasets are exhausted
Default: 'first_exhausted'.
shuffle_buffer_size: Buffer size for shuffling in streaming mode. Larger values
provide better randomization but use more memory. Default: 1000.
use_hf: Force using HuggingFace Hub (True) or ModelScope (False). If None,
it is controlled by the environment variable `USE_HF`, which defaults to '0'.
Default: None.
hub_token: Authentication token for accessing private datasets on the hub. Default: None.
strict: If True, raise exceptions when encountering malformed data rows.
If False, skip invalid rows with warnings. Default: False.
download_mode: How to handle existing cached datasets:
- 'reuse_dataset_if_exists': Use cached version if available
- 'force_redownload': Always download fresh copy
Default: 'reuse_dataset_if_exists'.
columns: Manual column name mapping for datasets. Dictionary mapping source column
names to target column names (e.g., {'text': 'content'}). Default: None.
remove_unused_columns: Whether to remove columns not used in preprocessing.
Helps reduce memory usage. Default: True.
disable_auto_column_mapping: By default, column names in the dataset are automatically
mapped. This parameter disables that behavior
(the `columns` parameter remains effective), defaulting to `False`.
model_name: Model name for self-cognition task preprocessing. Can be a tuple of
(Chinese_name, English_name) or list of names. Default: None.
model_author: Model author for self-cognition task preprocessing. Can be a tuple of
(Chinese_author, English_author) or list of authors. Default: None.
Returns:
A tuple of (train_dataset, val_dataset):
- train_dataset: The training dataset
- val_dataset: The validation dataset if split_dataset_ratio > 0, otherwise None
Examples:
>>> # Load single dataset
>>> train_ds, val_ds = load_dataset('AI-ModelScope/alpaca-gpt4-data-zh', split_dataset_ratio=0.1)
>>> # Load multiple datasets
>>> train_ds, _ = load_dataset(
... ['AI-ModelScope/alpaca-gpt4-data-zh#500', 'swift/self-cognition#500'],
... model_name=('我的模型', 'MyModel'),
... model_author=('作者', 'Author')
... )
"""
init_self_cognition_preprocessor(DATASET_MAPPING.get('self-cognition'), model_name, model_author)
if isinstance(datasets, str):
datasets = [datasets]
if not isinstance(seed, np.random.RandomState):
seed = np.random.RandomState(seed)
if streaming:
num_proc = None
train_datasets = []
val_datasets = []
use_hf_default = use_hf
if use_hf_default is None:
use_hf_default = True if use_hf_hub() else False
for dataset in datasets:
dataset_syntax = DatasetSyntax.parse(dataset)
use_hf = dataset_syntax.use_hf or use_hf_default
# compat dataset_name
if dataset_syntax.dataset in DATASET_MAPPING:
dataset_meta = DATASET_MAPPING[dataset_syntax.dataset]
if dataset_syntax.use_hf is None and dataset_meta.dataset_path is not None:
dataset_syntax.dataset = dataset_meta.dataset_path
dataset_syntax.dataset_type = 'path'
else:
dataset_syntax.dataset = dataset_meta.hf_dataset_id if use_hf else dataset_meta.ms_dataset_id
else:
dataset_meta = dataset_syntax.get_dataset_meta(use_hf)
loader = dataset_meta.loader(
num_proc=num_proc,
load_from_cache_file=load_from_cache_file,
streaming=streaming,
hub_token=hub_token,
strict=strict,
download_mode=download_mode,
columns=columns, # columns_mapping
remove_unused_columns=remove_unused_columns,
disable_auto_column_mapping=disable_auto_column_mapping,
)
train_dataset = loader.load(dataset_syntax, dataset_meta, use_hf=use_hf)
train_dataset, val_dataset = loader.post_process(
train_dataset,
dataset_sample=dataset_syntax.dataset_sample,
split_dataset_ratio=split_dataset_ratio,
streaming=streaming,
shuffle=shuffle,
random_state=seed,
)
if train_dataset is not None:
# Inject dataset_syntax.dataset as routing tag for multi-teacher
ds_name = dataset_syntax.dataset
train_dataset = _inject_dataset_routing_tag(train_dataset, ds_name)
train_datasets.append(train_dataset)
if val_dataset is not None:
ds_name = dataset_syntax.dataset
val_dataset = _inject_dataset_routing_tag(val_dataset, ds_name)
val_datasets.append(val_dataset)
if interleave_prob is None:
train_datasets = loader.concat_datasets(train_datasets)
val_datasets = loader.concat_datasets(val_datasets)
else:
train_datasets = loader.interleave_datasets(
train_datasets, interleave_prob, seed=get_seed(seed), stopping_strategy=stopping_strategy)
val_datasets = loader.interleave_datasets(
val_datasets, interleave_prob, seed=get_seed(seed), stopping_strategy=stopping_strategy)
if shuffle:
if train_datasets:
train_datasets = loader.shuffle_dataset(
train_datasets, seed=get_seed(seed), buffer_size=shuffle_buffer_size)
if val_datasets:
val_datasets = loader.shuffle_dataset(val_datasets, seed=get_seed(seed), buffer_size=shuffle_buffer_size)
return train_datasets, val_datasets
+128
View File
@@ -0,0 +1,128 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import aiohttp
import os
import shutil
from modelscope.hub.utils.utils import get_cache_dir
from typing import List, Literal, Optional, Union
from swift.utils import get_logger, safe_ddp_context
logger = get_logger()
class MediaResource:
"""A class to manage the resource downloading."""
cache_dir = os.path.join(get_cache_dir(), 'media_resources')
lock_dir = os.path.join(get_cache_dir(), 'lockers')
media_type_urls = {
'llava', 'coco', 'sam', 'gqa', 'ocr_vqa', 'textvqa', 'VG_100K', 'VG_100K_2', 'share_textvqa', 'web-celebrity',
'web-landmark', 'wikiart'
}
URL_PREFIX = 'https://www.modelscope.cn/api/v1/datasets/hjh0119/sharegpt4v-images/repo?Revision=master&FilePath='
@staticmethod
def get_url(media_type):
is_ocr_vqa = (media_type == 'ocr_vqa')
extension = 'tar' if is_ocr_vqa else 'zip'
return f'{MediaResource.URL_PREFIX}{media_type}.{extension}'
@staticmethod
def download(media_type_or_url: Union[str, List[str]],
local_alias: Optional[str] = None,
file_type: Literal['compressed', 'file', 'sharded'] = 'compressed'):
"""Download and extract a resource from a http link.
Args:
media_type_or_url: `str` or List or `str`, Either belongs to the `media_type_urls`
listed in the class field, or a remote url to download and extract.
Be aware that, this media type or url needs to contain a zip or tar file.
local_alias: `Options[str]`, The local alias name for the `media_type_or_url`. If the first arg is a
media_type listed in this class, local_alias can leave None. else please pass in a name for the url.
The local dir contains the extracted files will be: {cache_dir}/{local_alias}
file_type: The file type, if is a compressed file, un-compressed the file,
if is an original file, only download it, if is a sharded file, download all files and extract.
Returns:
The local dir contains the extracted files.
"""
media_file = media_type_or_url if isinstance(media_type_or_url, str) else media_type_or_url[0]
with safe_ddp_context(hash_id=media_file):
return MediaResource._safe_download(
media_type=media_type_or_url, media_name=local_alias, file_type=file_type)
@staticmethod
def move_directory_contents(src_dir, dst_dir):
if not os.path.exists(dst_dir):
os.makedirs(dst_dir)
for dirpath, dirnames, filenames in os.walk(src_dir):
relative_path = os.path.relpath(dirpath, src_dir)
target_dir = os.path.join(dst_dir, relative_path)
if not os.path.exists(target_dir):
os.makedirs(target_dir)
for file in filenames:
src_file = os.path.join(dirpath, file)
dst_file = os.path.join(target_dir, file)
shutil.move(src_file, dst_file)
@staticmethod
def _safe_download(media_type: Union[str, List[str]],
media_name: Optional[str] = None,
file_type: Literal['compressed', 'file', 'sharded'] = 'compressed'):
media_name = media_name or media_type
assert isinstance(media_name, str), f'{media_name} is not a str'
if isinstance(media_type, str) and media_type in MediaResource.media_type_urls:
media_type = MediaResource.get_url(media_type)
from datasets.download.download_manager import DownloadConfig, DownloadManager
final_folder = os.path.join(MediaResource.cache_dir, media_name)
if file_type == 'file':
filename = media_type.split('/')[-1]
final_path = os.path.join(final_folder, filename)
if os.path.exists(final_path): # if the download thing is a file but not folder,
return final_folder # check whether the file exists
if not os.path.exists(final_folder):
os.makedirs(final_folder) # and make sure final_folder exists to contain it
else:
if os.path.exists(final_folder):
return final_folder
logger.info('# #################Resource downloading#################')
logger.info('Downloading necessary resources...')
logger.info(f'Resource package: {media_type}')
logger.info(f'Extracting to local dir: {final_folder}')
logger.info('If the downloading fails or lasts a long time, '
'you can manually download the resources and extracting to the local dir.')
logger.info('Now begin.')
download_config = DownloadConfig(cache_dir=MediaResource.cache_dir)
download_config.storage_options = {'client_kwargs': {'timeout': aiohttp.ClientTimeout(total=86400)}}
if file_type == 'file':
filename = media_type.split('/')[-1]
final_path = os.path.join(final_folder, filename)
local_dirs = DownloadManager(download_config=download_config).download(media_type)
shutil.move(str(local_dirs), final_path)
elif file_type == 'compressed':
local_dirs = DownloadManager(download_config=download_config).download_and_extract(media_type)
shutil.move(str(local_dirs), final_folder)
else:
for media_url in media_type:
local_dirs = DownloadManager(download_config=download_config).download_and_extract(media_url)
MediaResource.move_directory_contents(str(local_dirs), final_folder)
logger.info('# #################Resource downloading finished#################')
return final_folder
@staticmethod
def safe_save(image, file_name, folder, format='JPEG'):
folder = os.path.join(MediaResource.cache_dir, folder)
os.makedirs(folder, exist_ok=True)
file = os.path.join(folder, file_name)
if os.path.exists(file):
return file
image.save(file, format=format)
return file
+230
View File
@@ -0,0 +1,230 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import math
import multiprocessing as mp
import torch.distributed as dist
from itertools import chain
from torch.utils.data import Dataset, IterableDataset
from tqdm import tqdm
from typing import Optional
from swift.template import MaxLengthError
from swift.utils import get_logger, is_dist, is_master, split_list
logger = get_logger()
def calculate_matched_group(sequences, packing_length: int, is_finished: bool = True, strategy: str = 'binpack'):
if len(sequences) == 0:
return [], []
if strategy == 'sequential':
# Order-preserving greedy packing (next-fit): keep a single open pack and flush it
# when the next sample doesn't fit, so the global sample order and pack boundaries
# follow the input order (a sequential sampler). (Use packing_num_proc=1 for
# a single global ordering.)
packs, cur, cur_len = [], [], 0
for item in sequences: # item = (idx, length); weight_pos=1 -> length at item[1]
seq_len = item[1]
if cur and cur_len + seq_len > packing_length:
packs.append(cur)
cur, cur_len = [], 0
cur.append(item)
cur_len += seq_len
if cur_len >= packing_length:
packs.append(cur)
cur, cur_len = [], 0
if is_finished:
if cur:
packs.append(cur)
return packs, []
return packs, cur
# default: best-fit-decreasing bin packing (https://arxiv.org/pdf/2404.10830)
import binpacking
sequences = binpacking.to_constant_volume(sequences, packing_length, weight_pos=1)
if sequences and not is_finished:
sequences, ret_sequences = sequences[:-1], sequences[-1]
else:
ret_sequences = []
return sequences, ret_sequences
class PackingDataset(Dataset):
PACKING_BATCH_SIZE = 1000
def __init__(
self,
template,
dataset,
num_proc: int = 1,
*,
strict: bool = False,
load_from_cache_file: bool = True,
packing_length: Optional[int] = None,
packing_num_proc: int = 1,
packing_strategy: str = 'binpack',
**kwargs,
):
template.packing = True
template.padding_free = True # TODO: remove
self.template = template
self.dataset = dataset
self.num_proc = num_proc
self.strict = strict
self.load_from_cache_file = load_from_cache_file
self.packing_strategy = packing_strategy
self.packing_length = packing_length or self.template.max_length
self.packing_num_proc = min(packing_num_proc, math.ceil(len(dataset) / self.PACKING_BATCH_SIZE))
self._out_queue = mp.Queue()
if is_master():
lengths = self.dataset['lengths']
offset = 0
chunked_lengths = split_list(lengths, self.packing_num_proc)
for i in range(self.packing_num_proc):
worker = mp.Process(
target=self.create_packed_idx, args=(
i,
offset,
chunked_lengths[i],
), daemon=True)
worker.start()
offset += len(chunked_lengths[i])
self.packed_idx = [[] for _ in range(self.packing_num_proc)]
self.packed_length = [[] for _ in range(self.packing_num_proc)]
desc = 'Packing: ' if self.packing_num_proc == 1 else f'Packing (num_proc={self.packing_num_proc}): '
with tqdm(total=len(lengths), dynamic_ncols=True, desc=desc) as prog_bar:
finished_workers = 0
while finished_workers < self.packing_num_proc:
rank, sequences, data_len = self._out_queue.get()
if data_len == -1:
finished_workers += 1
continue
prog_bar.update(data_len)
self.packed_idx[rank] += [[x[0] for x in seq] for seq in sequences]
self.packed_length[rank] += [sum(x[1] for x in seq) for seq in sequences]
self.packed_idx = list(chain.from_iterable(self.packed_idx))
self.packed_length = list(chain.from_iterable(self.packed_length))
else:
self.packed_idx, self.packed_length = None, None
if dist.is_initialized() and is_dist():
obj_list = [(self.packed_idx, self.packed_length)]
dist.broadcast_object_list(obj_list)
self.packed_idx, self.packed_length = obj_list[0]
def create_packed_idx(self, rank, offset, lengths):
data = [(i + offset, sum(length) if isinstance(length, list) else length) for i, length in enumerate(lengths)]
i = 0
input_data = []
while True:
new_data = data[i:i + self.PACKING_BATCH_SIZE]
input_data += new_data
if not input_data:
break
i += self.PACKING_BATCH_SIZE
is_finished = i >= len(data)
sequences, input_data = calculate_matched_group(
input_data, self.packing_length, is_finished=is_finished, strategy=self.packing_strategy)
self._out_queue.put((rank, sequences, len(new_data)))
self._out_queue.put((rank, [], -1))
def __getitem__(self, index):
sequence = self.packed_idx[index]
row = [self.dataset[i] for i in sequence]
return row
def __len__(self):
return len(self.packed_idx)
class IterablePackingDataset(IterableDataset):
def __init__(
self,
template,
dataset,
num_proc: int = 1,
*,
packing_interval: int = 128,
packing_length: Optional[int] = None,
strict: bool = False,
cyclic: bool = False,
packing_strategy: str = 'binpack',
**kwargs,
):
template.packing = True
template.padding_free = True # TODO: remove
self.template = template
self.dataset = dataset
self.num_proc = num_proc
self.strict = strict
self.packing_length = packing_length or self.template.max_length
self.packing_interval = packing_interval
self._in_queue = mp.Queue()
self._out_queue = mp.Queue()
self.workers = []
self.cyclic = cyclic
self.packing_strategy = packing_strategy
for _ in range(self.num_proc):
worker = mp.Process(target=self._processor, daemon=True)
worker.start()
self.workers.append(worker)
def _processor(self):
while True:
i, data = self._in_queue.get()
encoded_data = {}
try:
encoded_data = self.template.encode(data, return_length=True)
except Exception as e:
if self.strict and not isinstance(e, MaxLengthError):
raise
self._out_queue.put((i, encoded_data))
def _put_data_in_queue(self, iterator) -> int:
for i in range(self.packing_interval):
try:
data = next(iterator)
except StopIteration:
return i
self._in_queue.put((i, data))
return i + 1
def _fetch_data_out_queue(self, last_res, num_samples):
res = [None] * num_samples
for _ in range(num_samples):
i, data = self._out_queue.get()
if not data:
continue
res[i] = (data, len(data['input_ids']))
res = [data for data in res if data]
last_res += res
return last_res
@staticmethod
def cyclic_iter(iterable):
while True:
for x in iterable:
yield x
def __iter__(self):
try:
next(iter(self.dataset))
except StopIteration:
return
if self.cyclic:
iterator = self.cyclic_iter(self.dataset)
else:
iterator = iter(self.dataset)
data = []
while True:
num_samples = self._put_data_in_queue(iterator)
finished = num_samples != self.packing_interval
data = self._fetch_data_out_queue(data, num_samples)
sequences, data = calculate_matched_group(
data, self.packing_length, is_finished=finished, strategy=self.packing_strategy)
res = []
for row in sequences:
res.append([r[0] for r in row])
yield from res
if finished:
break
+4
View File
@@ -0,0 +1,4 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
from .core import (DATASET_TYPE, AlpacaPreprocessor, AutoPreprocessor, ClsPreprocessor, MessagesPreprocessor,
ResponsePreprocessor, RowPreprocessor)
from .extra import ClsGenerationPreprocessor, GroundingMixin, TextGenerationPreprocessor
+571
View File
@@ -0,0 +1,571 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import ast
import datasets
import numpy as np
import os
from collections import Counter
from contextlib import contextmanager
from datasets import Dataset as HfDataset
from datasets import Image
from datasets import IterableDataset as HfIterableDataset
from datasets import Sequence, Value
from itertools import chain
from modelscope.hub.utils.utils import get_cache_dir
from packaging import version
from typing import Any, Callable, Dict, List, Optional, Union
from swift.template import history_to_messages
from swift.utils import get_logger, is_dist, is_master, safe_ddp_context
DATASET_TYPE = Union[HfDataset, HfIterableDataset]
logger = get_logger()
_pair_keys = ['messages', 'images', 'videos', 'audios', 'tools', 'objects']
class RowPreprocessor:
standard_keys = _pair_keys + list(
chain.from_iterable([f'{prefix}_{k}' for k in _pair_keys]
for prefix in ['rejected', 'positive', 'negative'])) + [
'rejected_response',
'label',
'channel',
'margin',
'teacher_prompt',
'chat_template_kwargs',
# Qwen3-TTS
'ref_audios',
'audio_codes',
]
def __init__(self,
*,
columns: Optional[Dict[str, str]] = None,
dataset_sample: Optional[int] = None,
random_state: Optional[Union[np.random.RandomState, int]] = 42,
traceback_limit: int = 10) -> None:
self.columns = columns or {}
self.origin_columns = self.columns.copy() # Higher priority and raise Error
images_keys = ['images', 'image']
audios_keys = ['audios', 'audio']
videos_keys = ['videos', 'video']
for mm_type in ['images', 'audios', 'videos']:
keys = locals()[f'{mm_type}_keys']
for key in keys:
self.columns[key] = mm_type
self.traceback_limit = traceback_limit
self._traceback_counter = 0
self.dataset_sample = dataset_sample
self.datasets_4 = version.parse(datasets.__version__) >= version.parse('4.0')
if not isinstance(random_state, np.random.RandomState):
random_state = np.random.RandomState(random_state)
self.random_state = random_state
@staticmethod
def _check_messages(row: Dict[str, Any]) -> None:
if 'messages' not in row:
return
messages = row['messages']
assert len(messages) > 0, f'messages: {messages}'
# fix swift/SlimOrca (concat)
for message in messages:
keys = set(message.keys()) - {'role', 'content', 'loss', 'loss_scale'}
for key in keys:
message.pop(key)
for message in messages:
role, content = message['role'], message['content']
# The terms 'tool' and 'tool_response' have the same meaning, ensuring compatibility.
assert role in {'system', 'user', 'tool_call', 'tool_response', 'tool', 'assistant'}, f'message: {message}'
assert content is not None, f'message: {message}'
@staticmethod
def _cast_mm_data(row: Dict[str, Any]) -> None:
for key in ['images', 'rejected_images']:
images = row.get(key, None)
if images is None:
continue
if isinstance(images, str) or (isinstance(images, list) and images and isinstance(images[0], str)):
if isinstance(images, str):
images = [images]
for i, image in enumerate(images):
images[i] = {'bytes': None, 'path': image}
row[key] = images
elif isinstance(images, dict):
row[key] = [images]
for key in ['videos', 'audios']:
mm_data = row.get(key)
if mm_data is None:
continue
elif isinstance(mm_data, str):
row[key] = [mm_data]
@staticmethod
def _check_rejected_response(row: Dict[str, Any]) -> None:
if 'rejected_response' in row:
messages = row['messages']
rejected_response = row['rejected_response']
if (rejected_response is None
or isinstance(rejected_response, str) and rejected_response == messages[-1]['content']):
raise ValueError(f'rejected_response: {rejected_response}')
def preprocess(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
return row
def prepare_dataset(self, dataset: DATASET_TYPE) -> DATASET_TYPE:
return dataset
@staticmethod
def batched_to_rows(batched_row: Dict[str, Any]):
keys = list(batched_row.keys())
batch_size = len(batched_row[keys[0]])
return [{key: batched_row[key][i] for key in keys} for i in range(batch_size)]
@staticmethod
def rows_to_batched(rows: List[Dict[str, Any]]):
batched = {}
for i, row in enumerate(rows):
for k, v in row.items():
if k not in batched:
batched[k] = [None] * i
batched[k].append(v)
# Make all the lengths of v the same.
for k in set(batched.keys()) - set(row.keys()):
batched[k].append(None)
return batched
@staticmethod
def _remove_prefix_keys(row, prefix: str):
for k in list(row.keys()):
if k.startswith(prefix):
new_k = k[len(prefix):]
new_v = row.pop(k)
if new_k not in row:
row[new_k] = new_v
@staticmethod
def _check_objects(row):
objects = row.get('objects')
if objects is None:
return
new_objects = {}
# Ensure the order
for k in ['ref', 'bbox', 'bbox_type', 'image_id']:
if k in objects.keys():
new_objects[k] = objects[k]
row['objects'] = new_objects
bbox = new_objects['bbox']
# check bbox
for box in bbox:
assert len(box) in {2, 4}, f'len(box): {len(box)}'
if len(box) == 2:
continue
if box[0] > box[2]:
box[0], box[2] = box[2], box[0]
if box[1] > box[3]:
box[1], box[3] = box[3], box[1]
def batched_preprocess(self, batched_row: Dict[str, Any], *, strict: bool,
ignore_max_length_error: bool) -> Dict[str, Any]:
from swift.template import MaxLengthError
batched_row = dict(batched_row)
assert len(batched_row) > 0
self._remove_prefix_keys(batched_row, '__@') # compat streaming
rows = self.batched_to_rows(batched_row)
new_rows = []
for row in rows:
try:
row = self.preprocess(row)
# support [row1, row2, ...]
if row is None:
row = []
if isinstance(row, dict):
row = [row]
for r in row:
self._check_objects(r)
self._check_rejected_response(r)
self._check_messages(r)
self._cast_mm_data(r)
except Exception as e:
if strict:
logger.warning('To avoid errors, you can pass `strict=False`.')
raise
if isinstance(e, MaxLengthError) and ignore_max_length_error:
pass
elif self.traceback_limit is not None and self._traceback_counter < self.traceback_limit:
import traceback
logger.info(traceback.format_exc())
logger.warning('👆👆👆There are errors in the dataset, the data will be deleted')
self._traceback_counter += 1
row = []
new_rows += row
res = self.rows_to_batched(new_rows)
self._remove_prefix_keys(res, '__#') # compat GRPO
if len(res) == 0:
res['messages'] = []
return res
@staticmethod
def get_features_dataset(dataset: DATASET_TYPE) -> DATASET_TYPE:
if dataset.features is None:
assert isinstance(dataset, HfIterableDataset)
dataset = dataset._resolve_features()
return dataset
@staticmethod
def safe_rename_columns(dataset, columns):
dataset = RowPreprocessor.get_features_dataset(dataset)
columns_keys = {k.lower(): k for k in dataset.features.keys()} # lower -> lower/upper
safe_columns = {columns_keys[k.lower()]: v for k, v in columns.items() if k.lower() in columns_keys}
counter = Counter(safe_columns.values())
for k, new_k in list(safe_columns.items()):
if counter[new_k] > 1:
# For example, if "response" and "answer" match, then no processing is done.
safe_columns.pop(k)
continue
# e.g. Keep {'query': 'query'} to ensure that the query has the highest priority.
safe_columns = {k: v for k, v in safe_columns.items() if k != v}
if safe_columns:
dataset = dataset.rename_columns(safe_columns)
return dataset
@staticmethod
def remove_useless_columns(dataset: DATASET_TYPE) -> DATASET_TYPE:
dataset = RowPreprocessor.get_features_dataset(dataset)
features = dataset.features
k_list = [k for k in RowPreprocessor.standard_keys if k in features]
if len(k_list) != len(features):
dataset = dataset.select_columns(k_list)
return dataset
@contextmanager
def _patch_arrow_writer(self):
# fix AI-ModelScope/ms_agent_for_agentfabric:all
from datasets.arrow_writer import ArrowWriter
def _new_init(_self, schema=None, features=None, *args, **kwargs):
if features is not None:
if self.datasets_4:
from datasets.features import Json, List
messages_feature = List(Json())
for key in ['messages', 'rejected_messages', 'positive_messages', 'negative_messages']:
features[key] = messages_feature
features['images'] = List({'bytes': Value(dtype='binary'), 'path': Value(dtype='string')})
features['objects'] = Json()
features['chat_template_kwargs'] = Json()
else:
messages_feature = [{
'role': Value(dtype='string'),
'content': Value(dtype='string'),
}]
messages_feature_with_loss = [{
'role': Value(dtype='string'),
'content': Value(dtype='string'),
'loss': Value(dtype='bool'),
'loss_scale': Value(dtype='float64'),
}]
features['messages'] = messages_feature_with_loss
features['rejected_messages'] = messages_feature_with_loss
features['positive_messages'] = messages_feature
features['negative_messages'] = messages_feature
features['images'] = [{'bytes': Value(dtype='binary'), 'path': Value(dtype='string')}]
features['objects'] = {
'ref': Sequence(feature=Value(dtype='string'), length=-1),
'bbox': Sequence(feature=Sequence(feature=Value(dtype='float64'), length=-1), length=-1),
'bbox_type': Value(dtype='string'),
'image_id': Sequence(feature=Value(dtype='int64'), length=-1),
}
ArrowWriter.__origin_init__(_self, schema, features, *args, **kwargs)
ArrowWriter.__origin_init__ = ArrowWriter.__init__
ArrowWriter.__init__ = _new_init
try:
yield
finally:
ArrowWriter.__init__ = ArrowWriter.__origin_init__
del ArrowWriter.__origin_init__
def _cast_pil_image(self, dataset):
features = dataset.features
for col in ['images', 'rejected_images']:
if (col in features and isinstance(features[col], Image) and getattr(features[col], 'decode', False)):
dataset = dataset.cast_column(col, Image(decode=False))
return dataset
def __call__(
self,
dataset: DATASET_TYPE,
*,
num_proc: int = 1,
load_from_cache_file: bool = True,
strict: bool = False,
batch_size: Optional[int] = None,
enable_auto_mapping: bool = False,
) -> DATASET_TYPE:
from ..utils import sample_dataset
if batch_size is None:
batch_size = 1000 if isinstance(dataset, HfDataset) else 16
if self.dataset_sample is not None:
dataset = sample_dataset(dataset, self.dataset_sample, True, self.random_state)
map_kwargs = {'batched': True, 'batch_size': batch_size}
if isinstance(dataset, HfDataset):
if not load_from_cache_file and is_dist() and not is_master():
load_from_cache_file = True
map_kwargs.update({
'num_proc': num_proc,
'load_from_cache_file': load_from_cache_file,
})
# compat GRPO: The solution field will be retained.
dataset = RowPreprocessor.get_features_dataset(dataset)
if 'solution' in dataset.features:
with safe_ddp_context(None, True):
if isinstance(dataset, HfDataset) and not dataset.cache_files:
map_kwargs['cache_file_name'] = os.path.join(get_cache_dir(), 'datasets', 'map_cache',
f'{dataset._fingerprint}.arrow')
dataset = dataset.map(lambda x: {'__#solution': x['solution']}, **map_kwargs)
map_kwargs.pop('cache_file_name', None)
dataset = self.safe_rename_columns(dataset, self.origin_columns)
if enable_auto_mapping:
dataset = self.safe_rename_columns(dataset, self.columns)
dataset = self.prepare_dataset(dataset)
dataset = self._cast_pil_image(dataset)
if isinstance(dataset, HfIterableDataset):
# fix: https://github.com/huggingface/datasets/issues/6408
columns = {k: f'__@{k}' for k in RowPreprocessor.standard_keys if k in dataset.features}
if columns:
dataset = dataset.rename_columns(columns)
ignore_max_length_error = True
with self._patch_arrow_writer(), safe_ddp_context(None, True):
if isinstance(dataset, HfDataset) and not dataset.cache_files:
map_kwargs['cache_file_name'] = os.path.join(get_cache_dir(), 'datasets', 'map_cache',
f'{dataset._fingerprint}.arrow')
dataset_mapped = dataset.map(
self.batched_preprocess,
fn_kwargs={
'strict': strict,
'ignore_max_length_error': ignore_max_length_error,
},
remove_columns=list(dataset.features.keys()),
**map_kwargs)
if isinstance(dataset_mapped, HfDataset) and len(dataset) != len(dataset_mapped):
logger.info(
f'Dataset filtered, origin length: {len(dataset)}, filtered dataset length: {len(dataset_mapped)}')
return dataset_mapped
class ResponsePreprocessor(RowPreprocessor):
"""Dataset compatible with older versions of ms-swift"""
def __init__(self, *, columns: Optional[Dict[str, str]] = None, **kwargs) -> None:
super().__init__(columns=columns, **kwargs)
system_keys = ['system', 'system_prompt']
query_keys = ['query', 'prompt', 'input', 'instruction', 'question', 'problem']
response_keys = ['response', 'answer', 'output', 'targets', 'target', 'answer_key', 'answers', 'solution'
] + ['text', 'completion', 'content']
for key in system_keys:
self.columns[key] = 'system'
for key in query_keys:
self.columns[key] = 'query'
for key in response_keys:
self.columns[key] = 'response'
def preprocess(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
response = row.pop('response', None)
if response is not None:
if isinstance(response, (list, tuple)):
from transformers.utils import strtobool
# sometimes response is a list, pick one randomly
if strtobool(os.environ.get('RANDOM_DATASET_RESPONSE', 'False')):
response = self.random_state.choice(response)
else:
response = response[0]
history = row.pop('history', None) or []
query = row.pop('query', None)
system = row.pop('system', None)
if isinstance(history, str): # e.g. "[['query1', 'response1']]"
history = ast.literal_eval(history)
history.append([query, response])
row.update({'messages': history_to_messages(history, system)})
return row
class AlpacaPreprocessor(ResponsePreprocessor):
@classmethod
def concat_inst_input(cls, instruction, input_):
if instruction and input_:
query = f'{instruction}\n{input_}'
else:
query = instruction or input_
assert isinstance(query, str), f'query: {query}'
return query
def preprocess(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
instruction = row.pop('instruction', None)
input_ = row.pop('input', None)
output = row.pop('output', None)
if output is not None:
row['response'] = output
row['query'] = self.concat_inst_input(instruction, input_)
return super().preprocess(row)
def default_repair_messages(s: Union[str, Any]) -> Any:
if isinstance(s, str):
return ast.literal_eval(s)
return s
class MessagesPreprocessor(RowPreprocessor):
def __init__(
self,
*,
# If set to None, automatic matching will be performed.
role_key: Optional[str] = None, # 'role', 'from'
content_key: Optional[str] = None, # 'content', 'value'
user_role: Optional[str] = None, # 'user', 'human'
assistant_role: Optional[str] = None, # 'assistant', 'gpt', 'bot'
system_role: str = 'system',
# 'conversation', 'conversations' -> 'messages'
columns: Optional[Dict[str, str]] = None,
repair_messages: Callable[[Union[str, List[Dict[str, str]]]],
Optional[List[Dict[str, str]]]] = default_repair_messages,
inner_key: Optional[str] = None,
**kwargs):
super().__init__(columns=columns, **kwargs)
self.role_keys = ['role', 'from'] if role_key is None else [role_key]
self.content_keys = ['content', 'value'] if content_key is None else [content_key]
self.user_roles = ['user', 'human'] if user_role is None else [user_role]
self.assistant_roles = ['assistant', 'gpt', 'bot'] if assistant_role is None else [assistant_role]
self.tool_call_roles = ['function_call']
self.tool_response_roles = ['function_response', 'observation', 'observations']
self.system_role = system_role
self.repair_messages = repair_messages
self.inner_key = inner_key
message_keys = ['messages', 'conversation', 'conversations']
for key in message_keys:
self.columns[key] = 'messages'
# sharegptq
system_keys = ['system', 'system_prompt']
if system_role not in system_keys:
system_keys.append(system_role)
for key in system_keys:
self.columns[key] = 'system'
@staticmethod
def _is_sharegpt_format(message: Dict[str, str]) -> bool:
if 'role' in message or 'content' in message:
return False
return True
def sharegpt_to_messages(self, messages: List[Dict[str, str]], system: Optional[str]) -> List[Dict[str, str]]:
self._to_std_key(messages, 'user', self.user_roles)
self._to_std_key(messages, 'assistant', self.assistant_roles)
new_messages = []
if system is not None:
new_messages.append({'role': 'system', 'content': system})
for message in messages:
user_message = {'role': 'user', 'content': message['user']}
assistant_message = {'role': 'assistant', 'content': message['assistant']}
new_messages.append(user_message)
new_messages.append(assistant_message)
return new_messages
def to_std_messages(self, messages: List[Dict[str, str]], system: Optional[str]) -> None:
if messages[0]['role'] == self.system_role:
messages[0]['role'] = 'system'
elif system is not None:
messages.insert(0, {'role': 'system', 'content': system})
for message in messages:
role = message['role']
if role in self.user_roles:
message['role'] = 'user'
elif role in self.assistant_roles:
message['role'] = 'assistant'
elif role.replace('-', '_') in self.tool_call_roles:
message['role'] = 'tool_call'
elif role.replace('-', '_') in self.tool_response_roles:
message['role'] = 'tool_response'
@staticmethod
def _to_std_key(messages: List[Dict[str, str]], std_key: str, optional_keys: List[str]) -> None:
for message in messages:
for key in optional_keys:
if key in message:
message[std_key] = message.pop(key)
def preprocess(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
if 'rejected_messages' in row:
rejected = MessagesPreprocessor.preprocess(self, {'messages': row['rejected_messages']})
row['rejected_messages'] = rejected['messages'] if rejected else None
messages = row['messages']
if self.inner_key is not None:
messages = messages[self.inner_key]
messages: Optional[List[Dict[str, str]]] = self.repair_messages(messages)
if not messages or isinstance(messages, str):
return
self._to_std_key(messages, 'role', self.role_keys)
self._to_std_key(messages, 'content', self.content_keys)
system = row.pop('system', None)
if self._is_sharegpt_format(messages[0]):
messages = self.sharegpt_to_messages(messages, system)
else:
self.to_std_messages(messages, system) # inplace
row['messages'] = messages
return row
class ClsPreprocessor(ResponsePreprocessor):
def preprocess(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
res = super().preprocess(row)
res['label'] = int(res['label'])
return res
class AutoPreprocessor:
def __init__(self, *, columns: Optional[Dict[str, str]] = None, **kwargs) -> None:
self.columns = columns or {}
self.kwargs = kwargs
def _get_preprocessor(self, dataset: DATASET_TYPE) -> RowPreprocessor:
features = dataset.features
for key in ['conversation', 'conversations', 'messages']:
if key in features:
return MessagesPreprocessor(**self.kwargs)
if 'instruction' in features and 'input' in features:
return AlpacaPreprocessor(**self.kwargs)
return ResponsePreprocessor(**self.kwargs)
def __call__(
self,
dataset: DATASET_TYPE,
*,
num_proc: int = 1,
load_from_cache_file: bool = True,
**kwargs,
) -> DATASET_TYPE:
dataset = RowPreprocessor.safe_rename_columns(dataset, self.columns)
preprocessor = self._get_preprocessor(dataset)
return preprocessor(dataset, num_proc=num_proc, load_from_cache_file=load_from_cache_file, **kwargs)
+111
View File
@@ -0,0 +1,111 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import numpy as np
from typing import Any, Dict, List, Optional
from .core import ResponsePreprocessor
class GroundingMixin:
"""This class offers prompts to the grounding task"""
task_type: Optional[str] = None
_grounding_language_mixin = [0.8, 0.2]
_grounding_prompts = {
'grounding': {
'en': [('<ref-object>', '<bbox>'), ('The positions of <ref-object> is', '<bbox>'),
('Find the positions of <ref-object>', '<bbox>'), ('Where is <ref-object>', '<bbox>'),
('Find <ref-object>', '<bbox>'), ('Show me <ref-object>', '<bbox>'),
('Detect <ref-object>', '<bbox>'), ('Locate <ref-object>', '<bbox>'),
('Tell me the location of <ref-object>', '<bbox>'), ('Give the location of <ref-object>', '<bbox>'),
('Provide the bounding box coordinate of <ref-object>', '<bbox>')],
'zh': [('<ref-object>', '<bbox>'), ('<ref-object>的位置在图片中', '<bbox>'), ('<ref-object>在图片中', '<bbox>'),
('<ref-object>在', '<bbox>'), ('找到<ref-object>的位置', '<bbox>'), ('<ref-object>在哪里', '<bbox>'),
('提供<ref-object>的坐标位置', '<bbox>')]
},
'caption': {
'en': [
('<bbox>', '<ref-object>'),
('The object at position <bbox>', '<ref-object>'),
('This <bbox> is', '<ref-object>'),
('What is the object at <bbox>', '<ref-object>'),
('Describe <bbox>', '<ref-object>'),
('<bbox> is', '<ref-object>'),
('The bounding box coordinate <bbox> contains', '<ref-object>'),
],
'zh': [
('<bbox>', '<ref-object>'),
('<bbox>是什么', '<ref-object>'),
('<bbox>的位置包含', '<ref-object>'),
('描述<bbox>', '<ref-object>'),
('<bbox>中是', '<ref-object>'),
('坐标<bbox>描述了什么', '<ref-object>'),
('描述<bbox>中的事物', '<ref-object>'),
]
},
}
def construct_grounding_prompt(self):
# TODO Only support one bbox to one object
lang = np.random.choice(['en', 'zh'], p=[0.8, 0.2])
prompts = GroundingMixin._grounding_prompts[self.task_type][lang]
query, response = prompts[np.random.choice(range(len(prompts)))]
return query, response
class TextGenerationPreprocessor(ResponsePreprocessor):
def __init__(self,
*,
prompt: str,
query_tag: str = '{{QUERY}}',
columns: Optional[Dict[str, str]] = None,
**kwargs) -> None:
self.query_tag = query_tag
self.prompt = prompt
super().__init__(columns=columns, **kwargs)
def preprocess(self, row: Dict[str, Any]) -> Dict[str, Any]:
row['query'] = self.prompt.replace(self.query_tag, row['query'])
return super().preprocess(row)
class ClsGenerationPreprocessor(ResponsePreprocessor):
def __init__(self,
labels: List[str],
*,
task: str,
is_pair_seq: bool = False,
columns: Optional[Dict[str, str]] = None,
**kwargs) -> None:
self.labels = labels
self.task = task
self.is_pair_seq = is_pair_seq
category = ', '.join(labels)
self.sentence2_key = 'sentence2'
self.label_key = 'label'
if is_pair_seq:
self.sentence_key = 'sentence1'
inputs = 'Sentence1: {sentence1}\nSentence2: {sentence2}'
else:
self.sentence_key = 'sentence'
inputs = 'Sentence: {sentence}'
self.prompt = f"""Task: {task}
{inputs}
Category: {category}
Output:"""
super().__init__(columns=columns, **kwargs)
def preprocess(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
label = row.pop(self.label_key, None)
if label is None:
return
if self.is_pair_seq:
query = self.prompt.format(sentence1=row.pop(self.sentence_key), sentence2=row.pop(self.sentence2_key))
else:
query = self.prompt.format(sentence=row.pop(self.sentence_key))
row['query'] = query
row['response'] = self.labels[int(label)]
return super().preprocess(row)
+115
View File
@@ -0,0 +1,115 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import json
import os
from copy import deepcopy
from typing import Any, Dict, List, Optional, Union
from swift.utils import get_logger, use_hf_hub
from .dataset_meta import DATASET_MAPPING, DatasetMeta, SubsetDataset
from .preprocessor import AutoPreprocessor, MessagesPreprocessor
logger = get_logger()
def get_dataset_list():
datasets = []
for key in DATASET_MAPPING:
if use_hf_hub():
if key[1]:
datasets.append(key[1])
else:
if key[0]:
datasets.append(key[0])
return datasets
def register_dataset(dataset_meta: DatasetMeta, *, exist_ok: bool = False) -> None:
"""Register dataset
Args:
dataset_meta: The `DatasetMeta` info of the dataset.
exist_ok: If the dataset id exists, raise error or update it.
"""
if dataset_meta.dataset_name:
dataset_name = dataset_meta.dataset_name
else:
dataset_name = dataset_meta.ms_dataset_id, dataset_meta.hf_dataset_id, dataset_meta.dataset_path
if not exist_ok and dataset_name in DATASET_MAPPING:
raise ValueError(f'The `{dataset_name}` has already been registered in the DATASET_MAPPING.')
DATASET_MAPPING[dataset_name] = dataset_meta
def _preprocess_d_info(d_info: Dict[str, Any], *, base_dir: Optional[str] = None) -> Dict[str, Any]:
d_info = deepcopy(d_info)
columns = None
if 'columns' in d_info:
columns = d_info.pop('columns')
if 'messages' in d_info:
d_info['preprocess_func'] = MessagesPreprocessor(**d_info.pop('messages'), columns=columns)
else:
d_info['preprocess_func'] = AutoPreprocessor(columns=columns)
if 'dataset_path' in d_info:
dataset_path = d_info.pop('dataset_path')
if base_dir is not None and not os.path.isabs(dataset_path):
dataset_path = os.path.join(base_dir, dataset_path)
dataset_path = os.path.abspath(os.path.expanduser(dataset_path))
d_info['dataset_path'] = dataset_path
if 'subsets' in d_info:
subsets = d_info.pop('subsets')
for i, subset in enumerate(subsets):
if isinstance(subset, dict):
subsets[i] = SubsetDataset(**_preprocess_d_info(subset))
d_info['subsets'] = subsets
return d_info
def _register_d_info(d_info: Dict[str, Any], *, base_dir: Optional[str] = None) -> DatasetMeta:
"""Register a single dataset to dataset mapping
Args:
d_info: The dataset info
"""
d_info = _preprocess_d_info(d_info, base_dir=base_dir)
dataset_meta = DatasetMeta(**d_info)
register_dataset(dataset_meta)
return dataset_meta
def register_dataset_info(dataset_info: Union[str, List[str], None] = None) -> List[DatasetMeta]:
"""Register dataset from the `dataset_info.json` or a custom dataset info file
This is used to deal with the datasets defined in the json info file.
Args:
dataset_info: The dataset info path
"""
# dataset_info_path: path, json or None
if dataset_info is None:
dataset_info = os.path.join(os.path.dirname(__file__), 'data', 'dataset_info.json')
assert isinstance(dataset_info, (str, list))
base_dir = None
log_msg = None
if isinstance(dataset_info, str):
dataset_path = os.path.abspath(os.path.expanduser(dataset_info))
if os.path.isfile(dataset_path):
log_msg = dataset_path
base_dir = os.path.dirname(dataset_path)
with open(dataset_path, 'r', encoding='utf-8') as f:
dataset_info = json.load(f)
else:
dataset_info = json.loads(dataset_info) # json
if len(dataset_info) == 0:
return []
res = []
for d_info in dataset_info:
res.append(_register_d_info(d_info, base_dir=base_dir))
if log_msg is None:
log_msg = dataset_info if len(dataset_info) < 5 else list(dataset_info.keys())
logger.info(f'Successfully registered `{log_msg}`.')
return res
+153
View File
@@ -0,0 +1,153 @@
# Copyright (c) ModelScope Contributors. All rights reserved.
import inspect
import numpy as np
import os
import tempfile
from datasets import Dataset as HfDataset
from modelscope.hub.utils.utils import get_cache_dir
from torch.utils.data import Dataset
from typing import Any, Callable, Dict, Optional, Union
from swift.template import MaxLengthError, Template
from swift.utils import get_logger
from .preprocessor import RowPreprocessor
logger = get_logger()
def sample_dataset(
dataset: HfDataset,
dataset_sample: Optional[int],
shuffle: bool = True,
random_state: Optional[np.random.RandomState] = None,
shuffle_all: bool = False, # For compatibility, this defaults to False.
) -> HfDataset:
"""Sample dataset by a dataset_sample number
Args:
dataset: The dataset instance, iterable dataset is not supported
dataset_sample: The sample number
shuffle: Whether to perform random sampling on non-streaming datasets
random_state: The random state
Returns:
The sampled dataset
"""
if dataset_sample is None:
return dataset
n_repeat_sample = dataset_sample // len(dataset)
n_remain_sample = dataset_sample % len(dataset)
if n_repeat_sample >= 1 and n_remain_sample >= 1:
logger.warning(f'dataset_sample:{dataset_sample} is greater than len(dataset):{len(dataset)}, '
'repeated sampling will be performed.')
idx = np.tile(range(len(dataset)), n_repeat_sample)
if random_state is None:
random_state = np.random.RandomState()
if n_remain_sample >= 1:
if shuffle:
idx_remain = random_state.permutation(len(dataset))[:n_remain_sample]
else:
idx_remain = np.arange(n_remain_sample)
idx = np.concatenate([idx, idx_remain])
if n_repeat_sample >= 1 and shuffle and shuffle_all:
random_state.shuffle(idx)
dataset = dataset.select(idx)
return dataset
class LazyLLMDataset(Dataset):
"""This class if used to lazy tokenize the dataset, and skips bad ones when training"""
def __init__(self,
dataset: HfDataset,
encode_func: Callable[[Dict[str, Any]], Dict[str, Any]],
*,
n_try_fetch: int = 10,
strict: bool = False,
random_state: Optional[Union[np.random.RandomState, int]] = None,
traceback_limit: int = 10) -> None:
self.dataset = dataset
self.encode_func = encode_func
n_try_fetch = 1 if strict else min(n_try_fetch, len(self.dataset))
assert n_try_fetch >= 1
self.strict = strict
self.n_try_fetch = n_try_fetch
if not isinstance(random_state, np.random.RandomState):
random_state = np.random.RandomState(random_state)
self.random_state = random_state
self.traceback_limit = traceback_limit
self._traceback_counter = 0
self._idx = 0
self._idx_list = self.random_state.permutation(len(self.dataset)).tolist()
def __getitem__(self, idx: int) -> Dict[str, Any]:
if isinstance(idx, str):
return self.dataset[idx]
for i in range(self.n_try_fetch):
if i > 0:
idx = self._idx_list[self._idx]
self._idx = (self._idx + 1) % len(self.dataset)
data = self.dataset[idx]
try:
return self.encode_func(data, return_length=True)
except Exception as e:
if self.strict:
logger.warning('To avoid errors, you can pass `strict=False`.')
raise
if isinstance(e, MaxLengthError):
continue
if self.traceback_limit is not None and self._traceback_counter < self.traceback_limit:
import traceback
logger.info(traceback.format_exc())
logger.warning('👆👆👆There are errors in the template.encode, '
'and another piece of data will be randomly selected.')
self._traceback_counter += 1
raise ValueError('Failed to retrieve the dataset. You can avoid this issue by increasing `max_length` or '
'modifying the `truncation_strategy`.')
def __len__(self) -> int:
return len(self.dataset)
class EncodePreprocessor(RowPreprocessor):
def __init__(self, template: 'Template'):
super().__init__()
self.template = template
def preprocess(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
return self.template.encode(row, return_length=True)
class AddLengthPreprocessor(EncodePreprocessor):
def preprocess(self, row: Dict[str, Any]) -> Optional[Dict[str, Any]]:
encoded = super().preprocess(row)
row['lengths'] = encoded['lengths']
return row
TEMP_DIR_POOL = {}
def get_temporary_cache_files_directory(prefix=None):
if prefix is None:
import datasets.config
prefix = datasets.config.TEMP_CACHE_DIR_PREFIX
if prefix in TEMP_DIR_POOL:
TEMP_DIR = TEMP_DIR_POOL[prefix]
else:
tmp_dir = os.path.join(get_cache_dir(), 'tmp')
os.makedirs(tmp_dir, exist_ok=True)
kwargs = {}
parameters = inspect.signature(tempfile.TemporaryDirectory.__init__).parameters
if 'ignore_cleanup_errors' in parameters:
kwargs['ignore_cleanup_errors'] = True
TEMP_DIR = tempfile.TemporaryDirectory(prefix=prefix, dir=tmp_dir, **kwargs)
logger.info(f'create tmp_dir: {TEMP_DIR.name}')
TEMP_DIR_POOL[prefix] = TEMP_DIR
return TEMP_DIR.name