Files
microsoft--unilm/YOCO/yoco/tasks/gpt.py
T
2026-07-13 13:24:13 +08:00

177 lines
5.8 KiB
Python

import os
from typing import Optional
import json
from argparse import Namespace
import torch
from fairseq.tasks import register_task, FairseqDataclass, FairseqTask
from dataclasses import dataclass, field
from omegaconf import II
from .data.lm_loader import LMLoader
from .data.tiktoken_tokenizer import TiktokenTokenizer
from .data.llama_tokenizer import LLaMATokenizer
@dataclass
class GPTLanguageModelingConfig(FairseqDataclass):
data: Optional[str] = field(
default=None, metadata={"help": "path to data directory"}
)
tokens_per_sample: int = field(
default=1024,
metadata={"help": "max number of tokens per sample for LM dataset"},
)
max_target_positions: Optional[int] = field(
default=None, metadata={"help": "max number of tokens in the target sequence"}
)
llama_model: Optional[str] = field(
default=None,
metadata={"help": "path to load tokenizer and config"},
)
tiktoken_model: Optional[str] = field(
default=None,
metadata={
"help": "tiktoken model to tokenize the data"
},
)
batch_read_ahead: int = field(
default=10000,
metadata={"help": "batch read ahead size for infinibatch"},
)
pad_to_max_len: bool = field(
default=False,
metadata={"help": "pad each sentence to max length"},
)
absolute_path: bool = field(
default=False,
metadata={"help": "use absolute path in data config"},
)
tokenizer_pad_to_multiple: int = field(
default=8,
metadata={"help": "pad to multiple of this value"},
)
seed: int = II("common.seed")
batch_size: Optional[int] = II("dataset.batch_size")
@register_task('gpt', dataclass=GPTLanguageModelingConfig)
class GPTPretrainingTask(FairseqTask):
def __init__(self, args, tokenizer):
super().__init__(args)
self.cfg = args
self.tokenizer = tokenizer
@classmethod
def setup_task(cls, cfg, **kwargs):
"""Setup the task (e.g., load dictionaries).
Args:
args (argparse.Namespace): parsed command-line arguments
"""
if cfg.llama_model is not None:
tokenizer = LLaMATokenizer(os.path.join(cfg.llama_model, "tokenizer.model"))
elif cfg.tiktoken_model is not None:
tokenizer = TiktokenTokenizer(cfg.tiktoken_model, cfg.tokenizer_pad_to_multiple)
else:
raise ValueError("No tokenizer model provided")
return cls(cfg, tokenizer)
def load_dataset(self, split, epoch=1, combine=False, **kwargs):
self.datasets[split] = {
'data': json.load(open(f'{self.cfg.data}/json/{split}.json')),
'data_dir': self.cfg.data,
'shuffle': True if split == 'train' else False,
}
self.datasets[split] = Namespace(**self.datasets[split])
def dataset(self, split):
if split not in self.datasets:
raise KeyError("Dataset not loaded: " + split)
return self.datasets[split]
def get_batch_iterator(
self,
dataset,
max_tokens=None,
max_sentences=None,
max_positions=None,
ignore_invalid_inputs=False,
required_batch_size_multiple=1,
seed=1,
num_shards=1,
shard_id=0,
num_workers=0,
epoch=1,
data_buffer_size=0,
disable_iterator_cache=False,
skip_remainder_batch=False,
grouped_shuffling=False,
update_epoch_batch_itr=False
):
return LMLoader(
self.cfg,
dataset,
self.tokenizer,
max_tokens=max_tokens,
max_sentences=max_sentences,
max_positions=max_positions,
ignore_invalid_inputs=ignore_invalid_inputs,
required_batch_size_multiple=required_batch_size_multiple,
seed=seed,
epoch=epoch,
num_shards=num_shards,
shard_id=shard_id,
)
def train_step(
self, sample, model, criterion, optimizer, update_num, ignore_grad=False
):
"""
Do forward and backward, and return the loss as computed by *criterion*
for the given *model* and *sample*.
Args:
sample (dict): the mini-batch. The format is defined by the
:class:`~fairseq.data.FairseqDataset`.
model (~fairseq.models.BaseFairseqModel): the model
criterion (~fairseq.criterions.FairseqCriterion): the criterion
optimizer (~fairseq.optim.FairseqOptimizer): the optimizer
update_num (int): the current update
ignore_grad (bool): multiply loss by 0 if this is set to True
Returns:
tuple:
- the loss
- the sample size, which is used as the denominator for the
gradient
- logging outputs to display while training
"""
model.train()
model.set_num_updates(update_num)
with torch.autograd.profiler.record_function("forward"):
loss, sample_size, logging_output = criterion(model, sample)
if ignore_grad:
loss *= 0
with torch.autograd.profiler.record_function("backward"):
optimizer.backward(loss)
return loss, sample_size, logging_output
def valid_step(self, sample, model, criterion):
model.eval()
with torch.no_grad():
loss, sample_size, logging_output = criterion(model, sample)
return loss, sample_size, logging_output
@property
def target_dictionary(self):
padding_idx = self.tokenizer.pad_id
class Dict:
def pad(self):
return padding_idx
dictionary = Dict()
return dictionary