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

85 lines
4.0 KiB
Python

"""
Downloads and tokenizes the TinyShakespeare dataset.
- The download is from Github.
- The tokenization is GPT-2 tokenizer with tiktoken
The output is written to a newly created tinyshakespeare/ folder.
The script prints:
For GPT-2:
$ python dev/data/tinyshakespeare.py --model=gpt-2
writing 32,768 tokens to /home/ubuntu/llm.c/dev/data/tinyshakespeare/tiny_shakespeare_val.bin (66,560 bytes) in the gpt-2 format
writing 305,260 tokens to /home/ubuntu/llm.c/dev/data/tinyshakespeare/tiny_shakespeare_train.bin (611,544 bytes) in the gpt-2 format
For LLaMA 3:
$ python dev/data/tinyshakespeare.py --model=llama-3
writing 32,768 tokens to /home/ubuntu/llm.c/dev/data/tinyshakespeare/tiny_shakespeare_val.bin (132,096 bytes) in the llama-3 format
writing 276,224 tokens to /home/ubuntu/llm.c/dev/data/tinyshakespeare/tiny_shakespeare_train.bin (1,105,920 bytes) in the llama-3 format
And runs in a few seconds depending on your internet
connection and computer. The .bin files are raw byte
streams of uint16 (gpt-2) or uint32 (llama) numbers indicating the token ids.
"""
import argparse
import os
import tiktoken
from transformers import AutoTokenizer
from data_common import download_file, write_datafile
# -----------------------------------------------------------------------------
DATA_CACHE_DIR = os.path.join(os.path.dirname(__file__), "tinyshakespeare")
def download():
"""Downloads the TinyShakespeare dataset to DATA_CACHE_DIR"""
os.makedirs(DATA_CACHE_DIR, exist_ok=True)
# download the TinyShakespeare dataset, unless it's already downloaded
data_url = "https://raw.githubusercontent.com/karpathy/char-rnn/master/data/tinyshakespeare/input.txt"
data_filename = os.path.join(DATA_CACHE_DIR, "tiny_shakespeare.txt")
if not os.path.exists(data_filename):
print(f"Downloading {data_url} to {data_filename}...")
download_file(data_url, data_filename)
else:
print(f"{data_filename} already exists, skipping download...")
def tokenize(model_desc):
if model_desc == "gpt-2":
enc = tiktoken.get_encoding("gpt2")
encode = lambda s: enc.encode_ordinary(s)
eot = enc._special_tokens['<|endoftext|>'] # end of text token
elif model_desc == "llama-3":
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Meta-Llama-3.1-8B")
encode = lambda s: tokenizer.encode(s, add_special_tokens=False, verbose=False, split_special_tokens=True)
eot = tokenizer.encode('')[0] # by default the tokenizer adds the EOT token (128000)
else:
raise ValueError(f"unknown model descriptor {model_desc}")
data_filename = os.path.join(DATA_CACHE_DIR, "tiny_shakespeare.txt")
text = open(data_filename, 'r').read()
# let's treat every individual chunk of text as a separate "document"
sections = text.split("\n\n")
tokens = []
for i, s in enumerate(sections):
tokens.append(eot)
# there was a mild bug where I originally intended to remove \n\n, but instead just added
# the EOT right after each \n\n, so I'm keeping that behavior for backwards compatibility
# therefore we have to here add an extra \n\n at the end of each section, except the last
spad = s + "\n\n" if i != len(sections) - 1 else s
tokens.extend(encode(spad))
# let's take the first 32,768 tokens as the validation split (~10%)
val_tokens = tokens[:32768]
train_tokens = tokens[32768:]
# save to file
val_filename = os.path.join(DATA_CACHE_DIR, "tiny_shakespeare_val.bin")
train_filename = os.path.join(DATA_CACHE_DIR, "tiny_shakespeare_train.bin")
write_datafile(val_filename, val_tokens, model_desc)
write_datafile(train_filename, train_tokens, model_desc)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Tiny Shakespeare dataset preprocessing")
parser.add_argument("-m", "--model_desc", type=str, default="gpt-2", choices=["gpt-2", "llama-3"], help="Model type, gpt-2|llama-3")
args = parser.parse_args()
download()
tokenize(args.model_desc)