Skip to main content

A GPT-style large language model (LLM) with no positional embeddings (NoPE).

Project description

NoPE GPT

NoPE GPT is a generative pretrained Transformer-style (GPT) language model with no positional embeddings (NoPE). Built using PyTorch and trained on HuggingFace's Fineweb, SmolTalk, and UltraFeedback datasets, NoPE GPT can answer questions, summarize documents, use tools, and more.

Features

  • No positional embeddings (NoPE): NoPE GPT aims to be a more parsimonious model by completely removing positional embeddings from the architecture allowing the context length to vary without complex model surgery. Despite having no positional embeddings, NoPE GPT performs better at context length generalization than the best relative embeddings (ALiBi, RoPE, T5) offering good performance even when operating within 2X the trained context window.

  • Fast and memory-efficient: NoPE GPT employs a number of training and inference-time optimizations such as KV-caching, Group Query Attention, activation checkpointing, and Fully-sharded Data Parallel pretraining. As such, you can train and infer using relatively modest hardware.

  • Fully Open-source: Unlike closed-source LLMs, NoPE GPT provides both the model weights and the source code to train, fine-tune, export, and generate text from the model using your own hardware.

Pretrained Models

Name Vocab. Size Embedding Dim. Query Heads Key/Value Heads Hidden Ratio Layers Parameters
NoPE-GPT-400M-Chat 50,261 1280 20 5 4X 20 408M
NoPE-GPT-400M-Base 50,257 1280 20 5 4X 20 408M

Installation

The code required to run inference comes as a Python package that you can install with your favorite package manager such as pip.

pip install nope-gpt

Pretrained Examples

This first example we'll show how to load a pretrained base model from HuggingFace Hub and then use it to generate text. First, make sure the nope-gpt package is installed into your project. Once the package is installed you can load pretrained weights from HuggingFace Hub like in the example below.

from nope_gpt.model import NoPEGPT
from nope_gpt.tokenization import ChatTokenizer

model_name = "andrewdalpino/NoPE-GPT-400M-Base"

model = NoPEGPT.from_pretrained(model_name)

tokenizer = ChatTokenizer.from_pretrained(model_name)

Then, to generate text, provide a prompt, tokenize it, and iterate through the generate() method until the model outputs a stop token.

import torch

prompt = input("Enter a prompt: ")

prompt = tokenizer.tokenize(prompt)

prompt = torch.tensor(prompt, dtype=torch.int64)

for token, probability in model.generate(prompt):
    if token.item() in tokenizer.stop_tokens:
        break

    out = tokenizer.decode_single_token(token)

    print(out, end="", flush=True)

Generating text from the base model is the simplest way to get started with model inference, however, it is not the most useful when it comes to being able to chat with and guide the model output. In this example we'll load one of the pretrained chat models from HuggingFace Hub and then chat with it. In addition, we'll make use of short-term memory so the model can remember the chat history.

First, load a pretrained chat model from HuggingFace Hub like in the example below.

from nope_gpt.model import NoPEGPT
from nope_gpt.tokenization import ChatTokenizer

model_name = "andrewdalpino/NoPE-GPT-400M-Chat"

model = NoPEGPT.from_pretrained(model_name)

tokenizer = ChatTokenizer.from_pretrained(model_name)

Then, we'll define a partial function that will generate tokens with a set of default parameters such as max_tokens, context_length, and temperature.

from functools import partial

generate = partial(
    model.generate,
    max_tokens=2000,
    context_length=8192,
    temperature=0.7,
    top_k=500
    top_p=0.9,
    repeat_penalty=0.1,
    repeat_window=50,
)

Next, we'll instantiate a BufferWindowMemory object to handle the chat history and craft a system message that will guide generation. Note that messages are inputted as dicts with role and content keys. For a system message use the system role.

from nope_gpt.memory import BufferWindowMemory

memory = BufferWindowMemory(4)

system_message = {
    "role": "system",
    "content": "You are a friendly AI assistant.",
}

Finally, prompt the user for input, adds the system message and chat history to the context, tokenizes the messages, and then generates the assistant response.

import torch

while True:
    prompt = input("Enter a prompt: ")

    user_message = {
        "role": "user",
        "content": prompt,
    }

    memory.add_message(user_message)

    messages = [system_message] + memory.get_history()

    tokens = tokenizer.tokenize_prompt(messages)

    prompt = torch.tensor(tokens, dtype=torch.int64, device=args.device)

    response = ""

    for token, probability in generate(prompt):
        if token.item() in tokenizer.stop_tokens:
            break

        out = tokenizer.decode_single_token(token)

        print(out, end="", flush=True)

        response += out

    print("\n")

    assistant_message = {
        "role": "assistant",
        "content": response,
    }

    memory.add_message(assistant_message)

You're done! For more advanced usages take a look at the generate.py and chat.py scripts located in the code repository.

Training and Fine-tuning

In addition to the inference code, we also provide training and fine-tuning code so you can build your own NoPE GPT models. Before getting started, take a look at the model_sizing.ipynb IPython notebook in the project repo for a guide to sizing your model based on the amount of memory and compute you have available.

Clone the project repo

We'll need the code from the project repository to train and/or fine-tune the model.

git clone https://andrewdalpino/NoPE-GPT

Install Project Dependencies

Project dependencies are specified in the requirements.txt file. You can install them with pip using the following command from the project root. We recommend using a virtual environment such as venv to keep package dependencies on your system tidy.

python -m venv ./.venv

source ./.venv/bin/activate

pip install -r requirements.txt

Pretraining

Pretraining focuses on building a foundation of language and general knowledge to use as a base for future supervised fine-tuning. The training objective is to predict the next token in a sample of text. It is a self-supervised form of training because the model learns from masked inputs of unsupervised data. For the pretraining corpus we use the Fineweb dataset which consists of 15T high-quality tokens gathered from the worldwide web. In addition, the dataset has been split into 3 subsets (10BT, 100BT, and 350BT versions) for training smaller models.

python pretrain.py

Note that it will take a while to download and pre-process the dataset the first time that the training script is run.

To customize the default architecture you can adjust the embedding_dimensions, attention heads, num_hidden_layers, and feed_forward_ratio arguments of the pretraining script.

python pretrain.py --embedding_dimensions=4096 --num_q_heads=64 --num_kv_heads=16 --num_hidden_layers=48 --feed_forward_ratio=4

You can also adjust the batch_size, learning_rate, and gradient_accumulation_steps to suite your training setup.

python pretrain.py --batch_size=32 --learning_rate=0.01 --gradient_accumulation_steps=128

If you are planning a long training run, it is recommended to set a random seed. This will ensure that any random state is preserved if the process gets interrupted.

python pretrain.py --seed=42

For distributed training, use PyTorch's torchrun extension to launch a distributed data parallel (DDP) session. The example below is for executing the training script on a single node with 8 individual GPUs.

torchrun --standalone --nnodes=1 --nproc-per-node=8 pretrain.py --batch_size=16 --gradient_accumulation_steps=128

Note that when training in data-parallel mode it's important that the gradient_accumulation_steps divides evenly into the world size for maximum performance. For example, if we have an 8 GPU cluster, we could perform 32 gradient accumulation steps in exactly 4 passes over the network.

Pretraining Arguments

Argument Default Type Description
--dataset_subset "sample-10BT" str The subset of the Fineweb dataset to train on. Options are sample-10BT, sample-100BT, and sample-350BT. Set to None to train on the full 15T token dataset.
--token_encoding "r50k_base" str The Tiktoken encoding scheme to use when tokenizing the dataset. Options include r50k_base, p50k_base, cl100k_base, and o200k_base.
--dataset_path "./datasets" str The path to the preprocessed dataset files on disk.
--batch_size 2 int The number of samples of size tokens_per_sample to pass through the network at a time.
--gradient_accumulation_steps 128 int The number of batches to pass through the network before updating the model weights.
--tokens_per_sample 4096 int The number of tokens to pack into a single training sequence. This is sometimes called the block size or context length.
--max_steps 10000 int The maximum number of steps to take for pretraining.
--learning_rate 1e-2 float The learning rate of the Adafactor optimizer.
--low_memory_optimizer False bool Should the optimizer reduce its memory consumption in exchange for a slightly slower runtime?
--max_gradient_norm 10.0 float Clip gradients above this threshold norm before stepping.
--embedding_dimensions 1024 int The dimensionality of the token embeddings.
--num_q_heads 16 int The number of query heads within every attention layer.
--num_kv_heads 4 int The number of key and value heads within every attention layer.
--num_hidden_layers 16 int The number of attention/MLP blocks within the body of the network.
--feed_forward_ratio 4 (1, 2, 4) The ratio of hidden neurons to embedding dimensions in the MLP layers of the network.
--dropout 0.0 float The proportion of signals to send to zero during training as regularization.
--activation_checkpointing False bool Should we use activation checkpointing? This will drastically reduce memory utilization during training at the cost of recomputing the forward pass.
--ddp_sharding_level 2 int The level of sharding to use for DDP training. Options are 2 or 3 for partial and full sharding respectively, or 0 for no sharding.
--eval_interval 100 int Evaluate the model after this many epochs on the testing set.
--num_eval_samples 2048 int The number of hold-out samples to use for validation during training.
--checkpoint_interval 100 int Save the model checkpoint to disk every this many epochs.
--checkpoint_path "./checkpoints/checkpoint.pt" str The path to the base checkpoint file on disk.
--resume False bool Should we resume training from the last checkpoint?
--run_dir_path "./runs" str The path to the TensorBoard run directory for this training session.
--device "cpu" str The device to run the computation on.
--seed None int The seed for the random number generator.

Fine-tuning

Instruction-tuning is a supervised training technique focused on developing specialized objectives such as chatting, text summarization, chain-of-thought, and prompt rewriting. We use the SmolTalk and UltraFeedback datasets by HuggingFace as fine-tuning corpora because they include a broad range of training objectives such as conversation, instruction following, summarization, and human preference alignment.

python instruction-tune.py

To pick which dataset subsets to train on you can specify them in a comma-separated list like in the example below.

python instruction-tune.py --dataset_subsets=smol-magpie-ultra,smol-summarize,ultra-feedback

You can also adjust the batch_size, learning_rate, and gradient_accumulation_steps just like we did with pre-training.

python instruction-tune.py --batch_size=32 --learning_rate=0.01 --gradient_accumulation_steps=32

To adjust the number of trainable LoRA parameters as well as the strength of the LoRA and Dropout signals you can change the --rank, --alpha, and --dropout arguments respectively.

python instruction-tune.py --rank=4 --alpha=0.8 --dropout=0.1

Fine-tuning Arguments

Argument Default Type Description
--base_checkpoint_path None string The path to the base model checkpoint on disk.
--dataset_subset "all" str A comma-separated list of subsets of the dataset to train on. Options are all, apigen-80k, everyday-conversations, explore-instruct-rewriting, longalign, metamathqa-50k, numina-cot-100k, openhermes-100k, self-oss-instruct, smol-constraints, smol-magpie-ultra, smol-rewrite, smol-summarize, systemchats-30k, and ultra-feedback.
--max_tokens_per_sample 4096 int The maximum number of tokens to pack into a single training sequence.
--filter_long_samples False bool Should we filter out samples that are longer than the max_tokens_per_sample?
--num_dataset_processes 8 int The number of processes to use for processing the dataset.
--batch_size 2 int The number of samples to pass through the network at a time.
--gradient_accumulation_steps 64 int The number of batches to pass through the network before updating the weights.
--learning_rate 1e-2 float The learning rate of the Adafactor optimizer.
--low_memory_optimizer False bool Should the optimizer reduce its memory consumption in exchange for a slightly slower runtime?
--max_gradient_norm 1.0 float Clip gradients above this threshold norm before stepping.
--rank 8 int The rank of the LoRA decomposition matrices.
--alpha 1.0 float The strength of the LoRA signal.
--num_epochs 2 int The number of epochs to train for.
--activation_checkpointing False bool Should we use activation checkpointing? This will reduce drastically memory utilization during training at the cost of needing to recompute the forward pass.
--eval_interval 1 int Evaluate the model after this many epochs on the testing set.
--num_eval_samples 2048 int The number of hold-out samples to use for validation during training.
--checkpoint_interval 1 int Save the model parameters to disk every this many epochs.
--checkpoint_path "./checkpoints/checkpoint.pt" str The path to the model checkpoint.
--resume False bool Should we resume training from the last checkpoint?
--run_dir_path "./runs" str The path to the TensorBoard run directory for this training session.
--device "cuda" string The device to run the computation on.
--seed None int The seed for the random number generator.

Training Dashboard

We use TensorBoard to capture and display pretraining events such as loss and gradient norm updates. To launch the dashboard server run the following command from the terminal.

tensorboard --logdir=./runs

Then navigate to the dashboard using your favorite web browser.

References:

  • G. Penedo, et al. The FineWeb Datasets: Decanting the Web for the Finest Text Data at Scale, 38th Conference on Neural Information Processing Systems (NeurIPS 2024) Track on Datasets and Benchmarks.
  • L. B. Allal, et al. SmolLM2 - with great data, comes great performance, 2024.
  • A. Radford, et al. Language Models are Unsupervised Multitask Learners, OpenAI, 2019.
  • T. Brown, et al. Language Models are Few-Shot Learners. OpenAI, 2020.
  • A. Kazemnejad, et al. The Impact of Positional Encoding on Length Generalization in Transformers, 37th Conference on Neural Information Processing Systems (NeurIPS 2023).
  • S. Rajbhandari, et al. ZeRO: Memory Optimizations Toward Training Trillion Parameter Models, 2020.
  • J. R. Hermans, et al. Accumulated Gradient Normalization, JMLR: Workshop and Conference Proceedings, 2017.
  • T. Chen, et al. Training Deep Nets with Sublinear Memory Cost. MIT, 2019.
  • B. Zhang, et al. Root Mean Square Layer Normalization. 33rd Conference on Neural Information Processing Systems, NeurIPS 2019.
  • J. Kaplan, et al. Scaling Laws for Neural Language Models, OpenAI, 2020.
  • J. Hoffman, et al. Training Compute-Optimal Large Language Models, Deep Mind, 2022.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nope_gpt-0.1.1.tar.gz (20.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nope_gpt-0.1.1-py3-none-any.whl (15.3 kB view details)

Uploaded Python 3

File details

Details for the file nope_gpt-0.1.1.tar.gz.

File metadata

  • Download URL: nope_gpt-0.1.1.tar.gz
  • Upload date:
  • Size: 20.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for nope_gpt-0.1.1.tar.gz
Algorithm Hash digest
SHA256 57f74f46b6502d8687c75e5eaf9eb42d12e8b374752eea80674f0b166844a549
MD5 b62632fad262d9530935c78d4b4977c9
BLAKE2b-256 415fb761bc5e1d0105b08cc7aa1378dfb7b076be8abf3bc33ebf742aa9088155

See more details on using hashes here.

File details

Details for the file nope_gpt-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: nope_gpt-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 15.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for nope_gpt-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 949dea022bc939aa69468f6790f8fa5488667ca413d71b52fff35f57d42b2a26
MD5 085bed84718228b9572a4a2ee677c47c
BLAKE2b-256 895518c2782d588e7c3bf72d5d2503752f3cfd0d9adbb334dfbbd32cb91d02d8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page