Skip to content

Commit c4d5442

Browse files
artemruArtyom Kozhevnikov
andauthored
Main parquet text loader (#1220)
* add parquet based text dataloader with packing, plus minor refactors * missing file * using stric_state=False to avoid big buffer serialization * mypy * annotations * flake 8 * pinned_memory * comments * rm recipes * rm npc=1 * stricter split selection * adding split pattern * copy --------- Co-authored-by: Artyom Kozhevnikov <artyomko@fb.com>
1 parent 0b65822 commit c4d5442

9 files changed

Lines changed: 382 additions & 64 deletions

File tree

src/fairseq2/datasets/_config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ class DataReadOptions:
9797
num_prefetch: int = 1
9898
"""The number of batches to prefetch in background."""
9999

100+
npc: int = 10
101+
"""The reference number of parallel calls that data reader can do."""
102+
100103
seed: int = 2
101104
"""The seed to initialize the random number generators used internally."""
102105

src/fairseq2/datasets/instruction.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,6 @@ def splits(self) -> set[str]:
127127
"""Return the set of splits."""
128128

129129

130-
# TODO: FIX, INFER
131-
npc = 10
132-
133-
134130
GENERIC_INSTRUCTION_DATASET_FAMILY: Final = "generic_instruction"
135131

136132

@@ -238,8 +234,8 @@ def create_reader(
238234
source_encoder = tokenizer.create_encoder(mode=options.source_encode_mode)
239235
target_encoder = tokenizer.create_encoder(mode=options.target_encode_mode)
240236

241-
builder.map(source_encoder, selector="src", num_parallel_calls=npc)
242-
builder.map(target_encoder, selector="tgt", num_parallel_calls=npc)
237+
builder.map(source_encoder, selector="src")
238+
builder.map(target_encoder, selector="tgt")
243239

244240
def cat_source_and_target(example: dict[str, Any]) -> dict[str, Any]:
245241
id_ = example.get("id")
@@ -253,7 +249,7 @@ def cat_source_and_target(example: dict[str, Any]) -> dict[str, Any]:
253249

254250
return {"id": id_, "indices": indices, "target_mask": target_mask}
255251

256-
builder.map(cat_source_and_target, num_parallel_calls=npc)
252+
builder.map(cat_source_and_target)
257253

258254
batching = options.batching
259255

@@ -301,7 +297,7 @@ def skip(example: dict[str, Any]) -> bool:
301297
pad_value=tokenizer.vocab_info.pad_idx, overrides=[target_mask_collate_opts]
302298
)
303299

304-
builder.map(collater, num_parallel_calls=npc)
300+
builder.map(collater, num_parallel_calls=options.npc)
305301

306302
# Return only the first `max_num_batches`.
307303
if options.max_num_batches is not None:
@@ -373,7 +369,7 @@ def encode(example: dict[str, Any]) -> dict[str, Any]:
373369

374370
return {"id": id_, "prompt": source, "indices": indices}
375371

376-
builder.map(encode, num_parallel_calls=npc)
372+
builder.map(encode)
377373

378374
# Filter out long examples.
379375
def skip(example: dict[str, Any]) -> bool:
@@ -394,7 +390,7 @@ def skip(example: dict[str, Any]) -> bool:
394390
# Collate bucketed examples into a batch.
395391
collater = Collater(pad_value=tokenizer.vocab_info.pad_idx or 0)
396392

397-
builder.map(collater, num_parallel_calls=npc)
393+
builder.map(collater, num_parallel_calls=options.npc)
398394

399395
# Prefetch `num_prefetch` batches in background.
400396
builder.prefetch(options.num_prefetch)
@@ -421,7 +417,7 @@ def _read_jsonl(self, path: Path, tokenizer: TextTokenizer) -> DataPipelineBuild
421417
for line in fp:
422418
lines.append(line)
423419

424-
return read_sequence(lines).map(json.loads, num_parallel_calls=npc)
420+
return read_sequence(lines).map(json.loads)
425421

426422
@override
427423
def splits(self) -> set[str]:

src/fairseq2/datasets/jsonl.py

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,7 @@
1414
from torch import Tensor
1515
from typing_extensions import override
1616

17-
from fairseq2.data import (
18-
DataPipeline,
19-
read_sequence,
20-
)
17+
from fairseq2.data import DataPipeline, DataPipelineBuilder, read_sequence
2118
from fairseq2.data.text import read_text
2219
from fairseq2.data.text.tokenizers import TextTokenEncoder
2320
from fairseq2.datasets import (
@@ -27,15 +24,13 @@
2724
LengthBatching,
2825
SequenceBatch,
2926
)
30-
from fairseq2.datasets.text import TextDataset, TextReadOptions
27+
from fairseq2.datasets.text import GenericTextDataset, TextDataset, TextReadOptions
28+
from fairseq2.device import Device
3129
from fairseq2.error import NotSupportedError
3230
from fairseq2.gang import Gang
31+
from fairseq2.logging import log
3332
from fairseq2.nn import BatchLayout
3433

35-
# TODO: FIX, INFER
36-
npc = 10
37-
38-
3934
JSONL_DATASET_FAMILY: Final = "jsonl"
4035

4136

@@ -75,63 +70,109 @@ def create_reader(
7570
min_seq_len: int,
7671
max_seq_len: int,
7772
options: TextReadOptions | None = None,
73+
split: str | None = None,
7874
) -> DataReader[SequenceBatch]:
7975
if options is None:
8076
options = TextReadOptions()
8177

8278
file_rank = gang.rank
83-
8479
file_world_size = gang.size
8580

86-
if len(self._files) < file_world_size:
81+
text_column_name = options.extras.get("text_column_name", "text")
82+
assert isinstance(text_column_name, str)
83+
84+
if min_seq_len > 0:
85+
log.warning(
86+
f"The `min_seq_len={min_seq_len}` is ignored because of packing."
87+
)
88+
89+
split_pattern = options.extras.get("split_pattern", None)
90+
split_files = GenericTextDataset.filter_split(
91+
self._files,
92+
split,
93+
extention="jsonl",
94+
split_pattern=split_pattern, # type: ignore[arg-type]
95+
)
96+
97+
if len(split_files) < file_world_size:
8798
raise NotSupportedError(
8899
"The number of dataset files must be greater than or equal to the number of world size."
89100
)
90101

91-
builder = read_sequence(self._files)
102+
builder = read_sequence(split_files)
92103

93104
if file_world_size > 1:
94105
builder.shard(file_rank, file_world_size, allow_uneven=True)
95106

96107
def read_file(file: Path) -> DataPipeline:
97-
return read_text(file).map(json.loads, num_parallel_calls=1).and_return()
108+
return read_text(file).map(json.loads).and_return()
98109

99110
builder.yield_from(read_file)
100111

112+
pipeline = JsonlDataset.build_pipeline_backend(
113+
builder,
114+
options,
115+
text_encoder,
116+
pad_idx=pad_idx,
117+
max_seq_len=max_seq_len,
118+
text_column_name=text_column_name,
119+
device=gang.device,
120+
)
121+
return DataPipelineReader[SequenceBatch](
122+
self._name, "default", pipeline, gang, options
123+
)
124+
125+
@staticmethod
126+
def build_pipeline_backend(
127+
builder: DataPipelineBuilder,
128+
options: TextReadOptions,
129+
text_encoder: TextTokenEncoder,
130+
max_seq_len: int,
131+
pad_idx: int | None,
132+
text_column_name: str,
133+
device: Device,
134+
) -> DataPipeline:
135+
if pad_idx is None:
136+
pad_idx = 0
137+
138+
seed = options.seed
139+
if options.example_shuffle_window != 1:
140+
builder.shuffle(options.example_shuffle_window, seed)
141+
101142
# Tokenize.
102143
def encode(example: dict[str, Any]) -> Tensor:
103-
return text_encoder(example["text"])
144+
return text_encoder(example[text_column_name])
104145

105-
builder.map(encode, num_parallel_calls=1)
146+
builder.map(encode)
106147

107148
batching = options.batching
108149

109150
if isinstance(batching, LengthBatching):
110151
max_num_elements = batching.max_num_elements
111152

153+
pinned_memory = device.type == "cuda"
154+
# Pack.
112155
builder.pack(
113-
max_num_elements + 1, max_seq_len, truncate=True, pinned_memory=True
156+
max_num_elements + 1,
157+
max_seq_len,
158+
pad_value=pad_idx,
159+
truncate=True,
160+
pinned_memory=pinned_memory,
114161
)
162+
BatchLayout.compiled_max_seq_len = max_seq_len
115163
else:
116164
raise NotSupportedError(f"`{batching}` is not supported.")
117165

118-
BatchLayout.compiled_max_seq_len = max_seq_len
119-
120166
# Return only the first `max_num_batches`.
121167
if options.max_num_batches is not None:
122168
builder.take(options.max_num_batches)
123169

124-
# Prefetch `num_prefetch` batches in background.
125-
builder.prefetch(options.num_prefetch)
126-
127170
# Convert to `SequenceBatch`.
128171
def to_batch(example: dict[str, Any]) -> SequenceBatch:
129172
seqs, seq_lens = example["seqs"], example["seq_lens"]
130173

131174
return SequenceBatch(seqs, seq_lens, packed=True)
132175

133-
pipeline = builder.map(to_batch).and_return()
176+
pipeline = builder.map(to_batch).prefetch(options.num_prefetch).and_return()
134177

135-
return DataPipelineReader[SequenceBatch](
136-
self._name, "default", pipeline, gang, options
137-
)
178+
return pipeline

src/fairseq2/datasets/parallel_text.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,6 @@ def directions(self, split: str) -> list[Direction]:
105105
"""Return the directions included ``split``."""
106106

107107

108-
# TODO: FIX, INFER
109-
npc = 10
110-
111-
112108
GENERIC_PARALLEL_TEXT_DATASET_FAMILY: Final = "generic_parallel_text"
113109

114110

@@ -335,7 +331,7 @@ def encode(example: dict[str, Any]) -> dict[str, Any]:
335331

336332
return example
337333

338-
builder.map(encode, num_parallel_calls=npc)
334+
builder.map(encode)
339335

340336
batching = options.batching
341337

@@ -393,7 +389,7 @@ def skip(example: dict[str, Any]) -> bool:
393389
]
394390
)
395391

396-
builder.map(collater, num_parallel_calls=npc)
392+
builder.map(collater, num_parallel_calls=options.npc)
397393

398394
# Return only the first `max_num_batches`.
399395
if options.max_num_batches is not None:

src/fairseq2/datasets/preference.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,6 @@ def create_reader(
121121
"""
122122

123123

124-
# TODO: FIX, INFER
125-
npc = 10
126-
127-
128124
GENERIC_PREFERENCE_DATASET_FAMILY: Final = "generic_preference"
129125

130126

@@ -209,9 +205,9 @@ def create_reader(
209205
source_encoder = tokenizer.create_encoder(mode=options.source_encode_mode)
210206
target_encoder = tokenizer.create_encoder(mode=options.target_encode_mode)
211207

212-
builder.map(source_encoder, selector="src", num_parallel_calls=npc)
213-
builder.map(target_encoder, selector="tgt_chosen", num_parallel_calls=npc)
214-
builder.map(target_encoder, selector="tgt_rejected", num_parallel_calls=npc)
208+
builder.map(source_encoder, selector="src")
209+
builder.map(target_encoder, selector="tgt_chosen")
210+
builder.map(target_encoder, selector="tgt_rejected")
215211

216212
def cat_source_and_target(example: dict[str, Any]) -> dict[str, Any]:
217213
id_ = example.get("id", None)
@@ -264,7 +260,7 @@ def cat_source_and_target(example: dict[str, Any]) -> dict[str, Any]:
264260
"keep_jsonl_keys": jsonl_content,
265261
}
266262

267-
builder.map(cat_source_and_target, num_parallel_calls=npc)
263+
builder.map(cat_source_and_target)
268264

269265
batching = options.batching
270266

@@ -315,7 +311,7 @@ def skip(example: dict[str, Any]) -> bool:
315311

316312
collater = Collater(pad_value=0, overrides=target_mask_collate_opts)
317313

318-
builder.map(collater, num_parallel_calls=npc)
314+
builder.map(collater, num_parallel_calls=options.npc)
319315

320316
# Return only the first `max_num_batches`.
321317
if options.max_num_batches is not None:
@@ -387,7 +383,7 @@ def _read_jsonl(self, path: Path, tokenizer: TextTokenizer) -> DataPipelineBuild
387383
for line in fp:
388384
lines.append(line)
389385

390-
return read_sequence(lines).map(json.loads, num_parallel_calls=npc)
386+
return read_sequence(lines).map(json.loads)
391387

392388

393389
get_preference_dataset_hub = DatasetHubAccessor(PreferenceDataset)

0 commit comments

Comments
 (0)