minimind/course/impl/core/datasets.py

154 lines
6.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"""Dataset helpers to implement during Pretrain, SFT, and DPO stages."""
from __future__ import annotations
from torch.utils.data import Dataset
import random
from datasets import load_dataset, Features, Sequence, Value
import torch
def post_processing_chat(prompt_content, empty_think_ratio=0.2):
# 以80%概率移除空思考标签
if '<think>\n\n</think>\n\n' in prompt_content and random.random() > empty_think_ratio:
prompt_content = prompt_content.replace('<think>\n\n</think>\n\n', '')
return prompt_content
def pre_processing_chat(conversations, add_system_ratio=0.2):
# tool use 数据完整保留不做处理
if any(conv.get('tools') for conv in conversations): return conversations
SYSTEM_PROMPTS = [
"你是一个知识丰富的AI,尽力为用户提供准确的信息。",
"你是minimind,一个小巧但有用的语言模型。",
"你是一个专业的AI助手,请提供有价值的回答。",
"你是minimind,请尽力帮助用户解决问题。",
"你是一个可靠的AI,请给出准确的回答。",
"You are a helpful AI assistant.",
"You are minimind, a lightweight intelligent assistant.",
"You are a friendly chatbot. Please answer the user's questions carefully.",
"You are a knowledgeable AI. Try your best to provide accurate information.",
"You are minimind, a small but useful language model."
]
# 概率性添加system
if conversations[0].get('role') != 'system':
if random.random() < add_system_ratio:
return [{'role': 'system', 'content': random.choice(SYSTEM_PROMPTS)}] + conversations
return conversations
class CoursePretrainDataset(Dataset):
"""TODO: read jsonl text samples and return input_ids/labels.
Align with: dataset/lm_dataset.py::PretrainDataset
"""
def __init__(self, data_path: str, tokenizer, max_length: int):
self.data_path = data_path
self.tokenizer = tokenizer
self.max_length = max_length
features = Features({'conversations': [{'role': Value('string'), 'content': Value('string'), 'reasoning_content': Value('string'), 'tools': Value('string'), 'tool_calls': Value('string')}]})
self.samples=load_dataset('json', data_files=data_path, split='train', features=features)
self.bos_id=tokenizer(f'{tokenizer.bos_token}assistant\n',add_special_tokens=False).input_ids
self.eos_id=tokenizer(f'{tokenizer.eos_token}\n', add_special_tokens=False).input_ids
def __len__(self) -> int:
raise NotImplementedError("Implement in the Pretrain dataset lesson.")
def __getitem__(self, index: int):
raise NotImplementedError("Implement in the Pretrain dataset lesson.")
class CourseSFTDataset(Dataset):
"""TODO: render conversations and build SFT labels.
Align with: dataset/lm_dataset.py::SFTDataset
"""
def __init__(self, data_path: str, tokenizer, max_length: int):
self.data_path = data_path
self.tokenizer = tokenizer
self.max_length = max_length
features = Features({'conversations': [{'role': Value('string'), 'content': Value('string'), 'reasoning_content': Value('string'), 'tools': Value('string'), 'tool_calls': Value('string')}]})
self.samples=load_dataset('json', data_files=data_path, split='train', features=features)
self.bos_id=tokenizer(f'{tokenizer.bos_token}assistant\n',add_special_tokens=False).input_ids
self.eos_id=tokenizer(f'{tokenizer.eos_token}\n', add_special_tokens=False).input_ids
def create_chat_prompt(self, conversations):
"""TODO: render conversations with the tokenizer chat template.
Align with: dataset/lm_dataset.py::SFTDataset.create_chat_prompt
"""
messages=[]
tools=None
for message in conversations:
message=dict(message)
if message.get("role")=='system' and message.get("tools"):
tools = json.loads(message["tools"]) if isinstance(message["tools"], str) else message["tools"]
if message.get('tool_calls') and isinstance(message["tool_calls"], str):
message["tool_calls"] = json.loads(message["tool_calls"])
messages.append(message)
return self.tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=False,
tools=tools
)
raise NotImplementedError("Implement in the SFT training lesson.")
def generate_labels(self, input_ids: list[int]) -> list[int]:
"""TODO: keep assistant reply tokens and mask everything else as -100.
Align with: dataset/lm_dataset.py::SFTDataset.generate_labels
"""
labels=[-100]*len(input_ids)
i=0
while i<len(input_ids):
if input_ids[i:i+len(self.bos_id)]==self.bos_id:
start=i+len(self.bos_id)
end=start
while end<len(input_ids):
if input_ids[end:end+len(self.eos_id)]==self.eos_id:
break
end+=1
for j in range(start,min(end+len(self.eos_id),self.max_length)):
labels[j]=input_ids[j]
i=end+len(self.eos_id) if end < len(input_ids) else len(input_ids)
else:
i=i+1
return labels
raise NotImplementedError("Implement in the SFT training lesson.")
def __len__(self) -> int:
return len(self.samples)
raise NotImplementedError("Implement in the SFT dataset lesson.")
def __getitem__(self, index: int):
sample=self.samples[index]
conversations = pre_processing_chat(sample['conversations'])
prompt=self.create_chat_prompt(conversations)
prompt = post_processing_chat(prompt)
input_ids=self.tokenizer(prompt).input_ids[:self.max_length]
input_ids+=[self.tokenizer.pad_token_id]*(self.max_length-len(input_ids))
labels=self.generate_labels(input_ids)
return torch.tensor(input_ids, dtype=torch.long), torch.tensor(labels, dtype=torch.long)
raise NotImplementedError("Implement in the SFT dataset lesson.")
class CourseDPODataset(Dataset):
"""TODO: build chosen/rejected training pairs.
Align with: dataset/lm_dataset.py::DPODataset
"""
def __init__(self, data_path: str, tokenizer, max_length: int):
self.data_path = data_path
self.tokenizer = tokenizer
self.max_length = max_length
def __len__(self) -> int:
raise NotImplementedError("Implement in the DPO dataset lesson.")
def __getitem__(self, index: int):
raise NotImplementedError("Implement in the DPO dataset lesson.")