| """ |
| 数据集类 |
| """ |
| import os |
| import numpy as np |
| import torch |
| from torch.utils.data import Dataset, DataLoader |
|
|
|
|
| class PretrainDataset(Dataset): |
| """ |
| 预训练数据集 |
| 从二进制文件中读取token ids |
| """ |
|
|
| def __init__(self, data_file: str, block_size: int = 1024): |
| """ |
| Args: |
| data_file: 二进制数据文件路径 (.bin) |
| block_size: 序列长度 |
| """ |
| self.block_size = block_size |
|
|
| |
| self.data = np.memmap(data_file, dtype=np.uint16, mode='r') |
| self.num_tokens = len(self.data) |
|
|
| |
| self.num_samples = (self.num_tokens - 1) // block_size |
|
|
| print(f"Loaded {self.num_tokens:,} tokens from {data_file}") |
| print(f"Number of samples: {self.num_samples:,}") |
|
|
| def __len__(self): |
| return self.num_samples |
|
|
| def __getitem__(self, idx): |
| |
| start = idx * self.block_size |
| end = start + self.block_size + 1 |
|
|
| |
| chunk = torch.from_numpy(self.data[start:end].astype(np.int64)) |
|
|
| |
| x = chunk[:-1] |
| y = chunk[1:] |
|
|
| return {"input_ids": x, "labels": y} |
|
|
|
|
| class StreamingDataset(Dataset): |
| """ |
| 流式数据集,随机采样 |
| 适合大规模数据训练 |
| """ |
|
|
| def __init__( |
| self, |
| data_file: str, |
| block_size: int = 1024, |
| num_samples: int = None |
| ): |
| self.block_size = block_size |
| self.data = np.memmap(data_file, dtype=np.uint16, mode='r') |
| self.num_tokens = len(self.data) |
|
|
| |
| if num_samples is not None: |
| self.num_samples = num_samples |
| else: |
| self.num_samples = (self.num_tokens - 1) // block_size |
|
|
| def __len__(self): |
| return self.num_samples |
|
|
| def __getitem__(self, idx): |
| |
| max_start = self.num_tokens - self.block_size - 1 |
| start = np.random.randint(0, max_start) |
| end = start + self.block_size + 1 |
|
|
| chunk = torch.from_numpy(self.data[start:end].astype(np.int64)) |
|
|
| x = chunk[:-1] |
| y = chunk[1:] |
|
|
| return {"input_ids": x, "labels": y} |
|
|
|
|
| def get_dataloaders( |
| train_file: str, |
| val_file: str, |
| block_size: int = 1024, |
| train_batch_size: int = 4, |
| val_batch_size: int = 4, |
| num_workers: int = 4, |
| streaming: bool = False, |
| train_samples: int = None |
| ): |
| """ |
| 获取数据加载器 |
| |
| Args: |
| train_file: 训练数据文件 |
| val_file: 验证数据文件 |
| block_size: 序列长度 |
| train_batch_size: 训练batch size |
| val_batch_size: 验证batch size |
| num_workers: DataLoader worker数量 |
| streaming: 是否使用流式数据集 |
| train_samples: 训练样本数量(仅streaming模式有效) |
| """ |
| if streaming: |
| train_dataset = StreamingDataset(train_file, block_size, train_samples) |
| else: |
| train_dataset = PretrainDataset(train_file, block_size) |
|
|
| val_dataset = PretrainDataset(val_file, block_size) |
|
|
| train_loader = DataLoader( |
| train_dataset, |
| batch_size=train_batch_size, |
| shuffle=True, |
| num_workers=num_workers, |
| pin_memory=True, |
| drop_last=True |
| ) |
|
|
| val_loader = DataLoader( |
| val_dataset, |
| batch_size=val_batch_size, |
| shuffle=False, |
| num_workers=num_workers, |
| pin_memory=True, |
| drop_last=False |
| ) |
|
|
| return train_loader, val_loader |
|
|
|
|
| class DataCollatorForLM: |
| """ |
| 用于Hugging Face Trainer的数据整理器 |
| """ |
|
|
| def __call__(self, examples): |
| input_ids = torch.stack([ex["input_ids"] for ex in examples]) |
| labels = torch.stack([ex["labels"] for ex in examples]) |
|
|
| return { |
| "input_ids": input_ids, |
| "labels": labels |
| } |
|
|