中文基于Qwen3-235B-2507蒸餾數據集的操作
flyfish
方式1
from datasets import load_dataset
from transformers import AutoTokenizer# -------------------------- 配置參數 --------------------------
TOKENIZER_PATH = "/media/models/models/Qwen/Qwen3-8B/"
# 數據集存放路徑
DATASET_PATH = "/media/user/datasets/swift/Chinese-Qwen3-235B-2507-Distill-data-110k-SFT/qwen3_235b_2507_distill_110k.jsonl"# 最大序列長度(文本截斷/填充的目標長度)
MAX_SEQ_LENGTH = 1024
# 訓練集/驗證集大小(按需調整)
TRAIN_SIZE = 128
VAL_SIZE = 32# -------------------------- 加載分詞器 --------------------------
# 加載模型對應的分詞器(負責文本→token轉換)
tokenizer = AutoTokenizer.from_pretrained(TOKENIZER_PATH)
# 設置填充符號(Qwen等模型通常用eos_token作為pad_token)
tokenizer.pad_token = tokenizer.eos_token
# 設置填充方向為右側(語言模型通常右側填充更合理)
tokenizer.padding_side = "right"# -------------------------- 加載原始數據集 --------------------------
def load_raw_dataset(path):"""加載JSONL格式的原始數據集"""# 使用datasets庫加載JSONL文件(split="train"表示加載全部數據為訓練集)dataset = load_dataset("json", # 數據集格式為JSONdata_files=path, # 數據集文件路徑split="train" # 加載為"train"拆分(后續可再劃分))print(f"原始數據集加載完成,共 {len(dataset)} 條樣本")return dataset# -------------------------- 驗證數據格式 --------------------------
def check_data_format(dataset):"""驗證數據集是否包含正確的對話格式(messages字段)"""# 查看第一條樣本,確認結構是否正確sample = dataset[0]print("\n===== 樣本格式示例 =====")print(sample)# 檢查是否包含"messages"字段(對話數據核心)if "messages" not in sample:raise ValueError("數據集必須包含'messages'字段,格式應為[{role:..., content:...}, ...]")# 檢查messages內部結構(需包含role和content)for msg in sample["messages"]:if not all(key in msg for key in ["role", "content"]):raise ValueError("messages中的每條消息必須包含'role'和'content'字段")# -------------------------- 數據預處理(核心) --------------------------
def preprocess_dataset(dataset):"""對原始數據集進行預處理:1. 將對話格式化為模型可接受的文本(應用聊天模板)2. 轉換為token(input_ids)和標簽(labels)3. 過濾無效樣本"""def process_single_sample(example):"""處理單條樣本:格式化→tokenize"""try:# 1. 提取對話消息(如[{"role": "user", "content": "你好"}, {"role": "assistant",...}])messages = example["messages"]# 2. 應用聊天模板(將對話轉換為模型輸入格式)# 不同模型的聊天模板不同(如Qwen的格式為"<|im_start|>user...<|im_end|>assistant...")formatted_text = tokenizer.apply_chat_template(messages,tokenize=False, # 先不tokenize,僅生成格式化文本add_generation_prompt=False # 不添加生成提示(SFT訓練用))# 3. Tokenize:將文本轉換為模型可處理的整數序列(input_ids)tokenized = tokenizer(formatted_text,padding="max_length", # 填充到MAX_SEQ_LENGTHtruncation=True, # 超過長度則截斷max_length=MAX_SEQ_LENGTH,return_tensors="pt" # 返回PyTorch張量)# 4. 準備labels(與input_ids相同,自回歸訓練中用input_ids作為標簽)return {"input_ids": tokenized["input_ids"].squeeze(), # 去除多余維度"labels": tokenized["input_ids"].squeeze().clone() # 復制作為標簽}except Exception as e:# 跳過格式錯誤的樣本print(f"跳過無效樣本:{e}")return None# 批量處理所有樣本(使用多進程加速)processed = dataset.map(process_single_sample,remove_columns=dataset.column_names, # 移除原始字段(只保留處理后的結果)num_proc=4 # 4個進程并行處理)# 過濾處理失敗的樣本(返回None的樣本)processed = processed.filter(lambda x: x is not None)print(f"\n預處理完成,有效樣本數:{len(processed)}")return processed# -------------------------- 劃分訓練集與驗證集 --------------------------
def split_train_val(dataset, train_size, val_size):"""將預處理后的數據集劃分為訓練集和驗證集"""# 計算劃分比例(確保能截取到目標數量的樣本)split_ratio = (val_size + 1) / (train_size + val_size + 2)# 隨機劃分(seed確保結果可復現)split_data = dataset.train_test_split(test_size=split_ratio,seed=42)# 截取目標大小的樣本train_dataset = split_data["train"].select(range(train_size))val_dataset = split_data["test"].select(range(val_size))print(f"\n數據集劃分完成:訓練集 {len(train_dataset)} 條,驗證集 {len(val_dataset)} 條")return train_dataset, val_dataset# -------------------------- 主流程 --------------------------
if __name__ == "__main__":# 1. 加載原始數據集raw_dataset = load_raw_dataset(DATASET_PATH)# 2. 驗證數據格式是否正確check_data_format(raw_dataset)# 3. 預處理(格式化→tokenize)processed_dataset = preprocess_dataset(raw_dataset)# 4. 劃分訓練集和驗證集train_data, val_data = split_train_val(processed_dataset,train_size=TRAIN_SIZE,val_size=VAL_SIZE)# 查看處理后的樣本示例(input_ids的前10個token)print("\n===== 處理后樣本示例 =====")print("input_ids(前10個):", train_data[0]["input_ids"][:10])print("labels(前10個): ", train_data[0]["labels"][:10])
輸出
Generating train split: 110000 examples [00:01, 74385.42 examples/s]
原始數據集加載完成,共 110000 條樣本
===== 樣本格式示例 =====
{‘messages’: [{‘role’: ‘user’, ‘content’: ‘能給我講一個寓意深刻的故事嗎?’}, {‘role’: ‘assistant’, ‘content’: ‘當然可以。下面是一個寓意深刻的小故事,名為《石匠與神像》:\n\n很久以前,在一座深山里,有一位技藝精湛的石匠,名叫阿巖。他一生都在雕刻石頭,作品精美絕倫,遠近聞名。但他從不雕刻神像,別人問他為什么,他總是沉默不語。\n\n一天,村里要修建一座廟宇,村長請他雕刻一尊守護神像。阿巖猶豫了很久,終于答應了。他選了一塊巨大的青石,開始日復一日地鑿刻。每一錘、每一鑿,他都極為專注,仿佛在與石頭對話。\n\n三年過去了,神像終于完成。它面容莊嚴,目光深邃,仿佛能看透人心。村民們都驚嘆不已,紛紛跪拜,稱其為“活的神”。\n\n落成典禮那天,村長對阿巖說:“你創造了神,你就是神的化身。”\n\n阿巖卻搖了搖頭,輕聲說:“不,我不是創造者,我只是喚醒了它。”\n\n眾人不解。阿巖走到神像前,指著一處細微的裂痕說:“你們看,這塊石頭原本有一道裂紋,藏在深處。我本想避開它,可無論怎么雕,那裂痕總會出現在神像的眉心。最后我明白——它不是瑕疵,而是它本來的樣子。”\n\n他頓了頓,望向群山:“這石頭在山中沉睡了千年,經歷了風雨雷電,才有了這道傷痕。我所做的,不是塑造完美,而是讓傷痕也成為神圣的一部分。”\n\n眾人沉默。\n\n阿巖又說:“人總想雕刻出完美的神,可真正的神,不是無瑕的偶像,而是包容傷痕、接納殘缺的存在。我們每個人心中都有一塊石頭,有裂痕,有沉重,有沉默。但正是這些,讓我們真實,讓我們值得被喚醒。”\n\n說完,他放下刻刀,走進山林,再也沒有回來。\n\n——\n\n寓意: \n這個故事告訴我們,真正的美與力量,不在于完美無缺,而在于接納自己的傷痕與不完美。每個人都有裂痕,但那不是缺陷,而是經歷的印記,是靈魂的深度。我們不必成為別人眼中的“完美神像”,而是要勇敢地“喚醒”真實的自己——帶著傷痕,依然莊嚴站立。’}]}
Map (num_proc=4): 100%|110000/110000 [01:01<00:00, 1775.05 examples/s]
Filter: 100%|
110000/110000 [01:17<00:00, 1416.82 examples/s]預處理完成,有效樣本數:110000數據集劃分完成:訓練集 128 條,驗證集 32 條===== 處理后樣本示例 =====
input_ids(前10個): [151644, 872, 198, 100344, 107735, 77172, 41299, 110319, 33108, 112398]
labels(前10個): [151644, 872, 198, 100344, 107735, 77172, 41299, 110319, 33108, 112398]
方式2
from modelscope.msdatasets import MsDataset
dataset = MsDataset.load('swift/Chinese-Qwen3-235B-2507-Distill-data-110k-SFT')
print(f'dataset[0]: {dataset[0]}')
dataset[0]: {‘messages’: [{‘role’: ‘user’, ‘content’: ‘能給我講一個寓意深刻的故事嗎?’}, {‘role’: ‘assistant’, ‘content’: ‘當然可以。下面是一個寓意深刻的小故事,名為《石匠與神像》:\n\n很久以前,在一座深山里,有一位技藝精湛的石匠,名叫阿巖。他一生都在雕刻石頭,作品精美絕倫,遠近聞名。但他從不雕刻神像,別人問他為什么,他總是沉默不語。\n\n一天,村里要修建一座廟宇,村長請他雕刻一尊守護神像。阿巖猶豫了很久,終于答應了。他選了一塊巨大的青石,開始日復一日地鑿刻。每一錘、每一鑿,他都極為專注,仿佛在與石頭對話。\n\n三年過去了,神像終于完成。它面容莊嚴,目光深邃,仿佛能看透人心。村民們都驚嘆不已,紛紛跪拜,稱其為“活的神”。\n\n落成典禮那天,村長對阿巖說:“你創造了神,你就是神的化身。”\n\n阿巖卻搖了搖頭,輕聲說:“不,我不是創造者,我只是喚醒了它。”\n\n眾人不解。阿巖走到神像前,指著一處細微的裂痕說:“你們看,這塊石頭原本有一道裂紋,藏在深處。我本想避開它,可無論怎么雕,那裂痕總會出現在神像的眉心。最后我明白——它不是瑕疵,而是它本來的樣子。”\n\n他頓了頓,望向群山:“這石頭在山中沉睡了千年,經歷了風雨雷電,才有了這道傷痕。我所做的,不是塑造完美,而是讓傷痕也成為神圣的一部分。”\n\n眾人沉默。\n\n阿巖又說:“人總想雕刻出完美的神,可真正的神,不是無瑕的偶像,而是包容傷痕、接納殘缺的存在。我們每個人心中都有一塊石頭,有裂痕,有沉重,有沉默。但正是這些,讓我們真實,讓我們值得被喚醒。”\n\n說完,他放下刻刀,走進山林,再也沒有回來。\n\n——\n\n寓意: \n這個故事告訴我們,真正的美與力量,不在于完美無缺,而在于接納自己的傷痕與不完美。每個人都有裂痕,但那不是缺陷,而是經歷的印記,是靈魂的深度。我們不必成為別人眼中的“完美神像”,而是要勇敢地“喚醒”真實的自己——帶著傷痕,依然莊嚴站立。’}]}
方式3
from swift.llm import load_datasetdataset, val_dataset = load_dataset('swift/Chinese-Qwen3-235B-2507-Distill-data-110k-SFT')
print(f'dataset: {dataset}')
print(f'dataset[0]: {dataset[0]}')
輸出
dataset: Dataset({features: ['messages'],num_rows: 110000
})
dataset[0]: {‘messages’: [{‘role’: ‘user’, ‘content’: ‘能給我講一個寓意深刻的故事嗎?’}, {‘role’: ‘assistant’, ‘content’: ‘當然可以。下面是一個寓意深刻的小故事,名為《石匠與神像》:\n\n很久以前,在一座深山里,有一位技藝精湛的石匠,名叫阿巖。他一生都在雕刻石頭,作品精美絕倫,遠近聞名。但他從不雕刻神像,別人問他為什么,他總是沉默不語。\n\n一天,村里要修建一座廟宇,村長請他雕刻一尊守護神像。阿巖猶豫了很久,終于答應了。他選了一塊巨大的青石,開始日復一日地鑿刻。每一錘、每一鑿,他都極為專注,仿佛在與石頭對話。\n\n三年過去了,神像終于完成。它面容莊嚴,目光深邃,仿佛能看透人心。村民們都驚嘆不已,紛紛跪拜,稱其為“活的神”。\n\n落成典禮那天,村長對阿巖說:“你創造了神,你就是神的化身。”\n\n阿巖卻搖了搖頭,輕聲說:“不,我不是創造者,我只是喚醒了它。”\n\n眾人不解。阿巖走到神像前,指著一處細微的裂痕說:“你們看,這塊石頭原本有一道裂紋,藏在深處。我本想避開它,可無論怎么雕,那裂痕總會出現在神像的眉心。最后我明白——它不是瑕疵,而是它本來的樣子。”\n\n他頓了頓,望向群山:“這石頭在山中沉睡了千年,經歷了風雨雷電,才有了這道傷痕。我所做的,不是塑造完美,而是讓傷痕也成為神圣的一部分。”\n\n眾人沉默。\n\n阿巖又說:“人總想雕刻出完美的神,可真正的神,不是無瑕的偶像,而是包容傷痕、接納殘缺的存在。我們每個人心中都有一塊石頭,有裂痕,有沉重,有沉默。但正是這些,讓我們真實,讓我們值得被喚醒。”\n\n說完,他放下刻刀,走進山林,再也沒有回來。\n\n——\n\n寓意: \n這個故事告訴我們,真正的美與力量,不在于完美無缺,而在于接納自己的傷痕與不完美。每個人都有裂痕,但那不是缺陷,而是經歷的印記,是靈魂的深度。我們不必成為別人眼中的“完美神像”,而是要勇敢地“喚醒”真實的自己——帶著傷痕,依然莊嚴站立。’}]}