Files
2026-07-13 12:37:59 +08:00

122 lines
4.8 KiB
Python

"""
Common utilities for the datasets
"""
import requests
from tqdm import tqdm
import numpy as np
def download_file(url: str, fname: str, chunk_size=1024):
"""Helper function to download a file from a given url"""
resp = requests.get(url, stream=True)
total = int(resp.headers.get("content-length", 0))
with open(fname, "wb") as file, tqdm(
desc=fname,
total=total,
unit="iB",
unit_scale=True,
unit_divisor=1024,
) as bar:
for data in resp.iter_content(chunk_size=chunk_size):
size = file.write(data)
bar.update(size)
HEADERS_INFO = {
"gpt-2": {
"magic": 20240520,
"version": 1,
"token_dtype": np.uint16,
},
"llama-3": {
"magic": 20240801,
"version": 7,
"token_dtype": np.uint32,
},
}
def write_datafile(filename, toks, model_desc="gpt-2"):
"""
Saves token data as a .bin file, for reading in C.
- First comes a header with 256 int32s
- The tokens follow, each as uint16 (gpt-2) or uint32 (llama)
"""
assert len(toks) < 2**31, "token count too large" # ~2.1B tokens
assert model_desc in ["gpt-2", "llama-3"], f"unknown model descriptor {model_desc}"
info = HEADERS_INFO[model_desc]
# construct the header
header = np.zeros(256, dtype=np.int32) # header is always 256 int32 values
header[0] = info["magic"]
header[1] = info["version"]
header[2] = len(toks) # number of tokens after the 256*4 bytes of header
# construct the data (numpy array of tokens)
toks_np = np.array(toks, dtype=info["token_dtype"])
# write to file
num_bytes = (256 * 4) + (len(toks) * toks_np.itemsize)
print(f"writing {len(toks):,} tokens to {filename} ({num_bytes:,} bytes) in the {model_desc} format")
with open(filename, "wb") as f:
f.write(header.tobytes())
f.write(toks_np.tobytes())
def write_evalfile(filename, datas):
"""
Saves eval data as a .bin file, for reading in C.
Used for multiple-choice style evals, e.g. HellaSwag and MMLU
- First comes a header with 256 int32s
- The examples follow, each example is a stream of uint16_t:
- <START_EXAMPLE> delimiter of 2**16-1, i.e. 65,535
- <EXAMPLE_BYTES>, bytes encoding this example, allowing efficient skip to next
- <EXAMPLE_INDEX>, the index of the example in the dataset
- <LABEL>, the index of the correct completion
- <NUM_COMPLETIONS>, indicating the number of completions (usually 4)
- <NUM><CONTEXT_TOKENS>, where <NUM> is the number of tokens in the context
- <NUM><COMPLETION_TOKENS>, repeated NUM_COMPLETIONS times
"""
# construct the header
header = np.zeros(256, dtype=np.int32)
header[0] = 20240522 # magic
header[1] = 1 # version
header[2] = len(datas) # number of examples
header[3] = 0 # reserved for longest_example_bytes, fill in later
# now write the individual examples
longest_example_bytes = 0 # in units of uint16s
full_stream = [] # the stream of uint16s, we'll write a single time at the end
assert len(datas) < 2**16, "too many examples?"
for idx, data in enumerate(datas):
stream = []
# header of the example
stream.append(2**16-1) # <START_EXAMPLE>
stream.append(0) # <EXAMPLE_BYTES> (fill in later)
stream.append(idx) # <EXAMPLE_INDEX>
stream.append(data["label"]) # <LABEL>
ending_tokens = data["ending_tokens"]
assert len(ending_tokens) == 4, "expected 4 completions for now? can relax later"
stream.append(len(ending_tokens)) # <NUM_COMPLETIONS>
# the (shared) context tokens
ctx_tokens = data["ctx_tokens"]
assert all(0 <= t < 2**16-1 for t in ctx_tokens), "bad context token"
stream.append(len(ctx_tokens))
stream.extend(ctx_tokens)
# the completion tokens
for end_tokens in ending_tokens:
assert all(0 <= t < 2**16-1 for t in end_tokens), "bad completion token"
stream.append(len(end_tokens))
stream.extend(end_tokens)
# write to full stream
nbytes = len(stream)*2 # 2 bytes per uint16
assert nbytes < 2**16, "example too large?"
stream[1] = nbytes # fill in the <EXAMPLE_BYTES> field
longest_example_bytes = max(longest_example_bytes, nbytes)
full_stream.extend(stream)
# construct the numpy array
stream_np = np.array(full_stream, dtype=np.uint16)
# fill in the longest_example field
assert 0 < longest_example_bytes < 2**16, f"bad longest_example"
header[3] = longest_example_bytes
# write to file (for HellaSwag val this is 10,042 examples, 3.6MB file)
print(f"writing {len(datas):,} examples to {filename}")
with open(filename, "wb") as f:
f.write(header.tobytes())
f.write(stream_np.tobytes())