A holistic way of understanding how Llama and its components run in practice, with code and detailed documentation (GitHub Pages | GitHub). "The nuts and bolts" (practical side instead of theoretical facts, pure implementation details) of required components, infrastructure, and mathematical operations without using external dependencies or libraries.
This project intentionally doesn't have support for GPGPU (such as nVidia CUDA, OpenCL) as well as SIMD because it doesn't aim to be a production application, for now. Instead, the project relies on CPU cores to perform all mathematical operations, including linear algebraic computations. To increase performance, the code has been optimized as much as necessary, utilizing parallelization via goroutines.
Llama Nuts and Bolts Screen Recording GIF, captured while the application was running on the Apple MacBook Pro M1 Chip. Predefined prompts within the application were executed. The GIF is 20x faster.
This project was developed for only educational purposes, and has not been tested for production or commercial usage. The goal is to make an experimental project that can perform inference on the Llama 3.1 8B-Instruct model completely outside of the Python ecosystem. Throughout this journey, the aim is to acquire knowledge and shed light on the abstracted internal layers of this technology.
This journey is an intentional journey of literally reinventing the wheel. While reading this journey in the documentation directory (GitHub Pages | GitHub), you will navigate toward the target with a deductive flow. You will encounter the same stops and obstacles I encountered during this journey.
If you are curious like me about how the LLMs (Large Language Models) and transformers work and have delved into conceptual explanations and schematic drawings in the sources but hunger for deeper understanding, then this project is perfect for you too!
The journey can be found documented step by step at Llama Nuts and Bolts - GitHub Pages website with a visually better experience, or at docs directory.
Due to any of the existing libraries (except the built-in packages and a few helpers) wasn't used, all of the required functions were implemented by this project in the style of Go. However, the main goal of this project is to do inference only on the Llama 3.1 8B-Instruct model, the functionality fulfills only the requirements of this specific model. Not much, not less, because the goal of our project is not to be a production-level tensor framework.
The project provides a CLI (command line interface) application allowing users to choose from predefined prompts or write custom prompts. It then performs inference on the model and displays the generated text on the console. The application supports "streaming," enabling immediate display of generated tokens on the screen without waiting for the entire process to complete.
As you can see in the chapters in the documentation directory (GitHub Pages | GitHub), covered things are:
- All diagrams about the model and the flow are listed in Chapter 20,
- Parallelization and concurrency in Go, see Chapter 13,
- Implementing Memory Mapping that allows us to map a large file content to memory address space in Go for both Linux/MacOS and Windows platforms, see Chapter 2,
- Implementing BFloat16 (Brain Floating Point) data type which isn't supported by the Go language, from scratch, see Chapter 7,
- Implementing support for "streaming" output via Go channels, see Chapter 13,
- Loading a PyTorch model weights file ("consolidated.00.pth") which was saved as Pickle (.pkl) format, from scratch, see Chapter 2 and Chapter 3,
- Loading the model arguments JSON file ("params.json"), see Chapter 4,
- Loading a Byte-Pair Encoding (BPE) tokenizer model which was saved as Tiktoken tokenizer format file ("tokenizer.model"), from scratch, see Chapter 5,
- Implementing a Tensor type, tensor aritmetic and machine learning mathematical operation functions, see Chapter 8,
- Working with C contiguous arrays in multi-dimensional form, see Chapter 8,
- Building the blocks of Llama 3 model architecture, see Chapter 9,
- Implementing RoPE (Rotary Positional Embeddings) and precomputing frequency tensor, see Chapter 10 and Chapter 10.BONUS,
- Understanding tokens, vocabulary, and tokenization, see Chapter 12,
- Generating the next token, internals of transformer block, being an auto-regressive model, multi-head self-attention, and much more, see Chapter 13, Chapter 14, Chapter 15, Chapter 16,
- Understanding internals of the Unicode Standard, UTF-8 encoding, and how emojis are represented and rendered, see Chapter 17,
- And, so much more!
This diagram has extensive and comprehensive content and tries to reveal all of the details as much as possible. These reasons made it to have a considerable length. To use the space here efficiently, it was enclosed within an expandable section as follows:
Click to expand to see the Complete Model Diagram
The whole flow of Llama 3.1 8B-Instruct model without abstraction:
Llama model weights files can be found in several formats on the Internet. Meta's official format, HuggingFace format, GGUF format, etc... But our project uses only the official format.
Note: Download chapter of original Llama 3.1 package repository and this How to Install Llama 2 Locally article (disclaimer: there are some differences between this article and Llama 3.1) may help you too.
Click to expand the details of downloading instructions
-
Request access from Meta Website https://llama.meta.com/llama-downloads/ by filling the form. The email address must be valid and it's enough to mark "Llama 3.1" checkbox in the model list,
-
You will receive an email from Meta including the instructions for downloading the files,
-
Download the download.sh file from the official Llama repository,
-
Create a parent directory, and copy the
download.sh
script there:$ mkdir llama-models $ cd llama-models
-
Give executable permission to the download.sh file:
llama-models$ chmod +x download.sh
-
Pre-requisites: Make sure you have
wget
andmd5sum
installed, -
Run the script:
llama-models$ ./download.sh
-
The download script asks for the URL was come with the email:
llama-models$ ./download.sh Enter the URL from email:
-
Copy the URL written after
When asked for your unique custom URL, please insert the following
text in the email, and press ENTER, -
It asks for the model names that you want to download:
llama-models$ ./download.sh Enter the URL from email: # URL was entered and ENTER was pressed **** Model list *** - meta-llama-3.1-405b - meta-llama-3.1-70b - meta-llama-3.1-8b - meta-llama-guard-3-8b - prompt-guard Choose the model to download:
-
Write
meta-llama-3.1-8b
and press ENTER, be careful on writing as it is because it is case-sensitive, -
It asks for the sub-model name:
Choose the model to download: meta-llama-3.1-8b Selected model: meta-llama-3.1-8b **** Available models to download: *** - meta-llama-3.1-8b-instruct - meta-llama-3.1-8b Enter the list of models to download without spaces or press Enter for all:
-
Write
meta-llama-3.1-8b-instruct
and press ENTER, be careful on writing as it is because it is case-sensitive, -
If everyting goes well, you will see a progress like:
... Enter the list of models to download without spaces or press Enter for all: meta-llama-3.1-8b-instruct Downloading LICENSE and Acceptable Usage Policy ...
-
If you get HTTP 403 error:
- Check if you wrote the model name correctly as
meta-llama-3.1-8b
and thenmeta-llama-3.1-8b-instruct
- Check if the download link you copied ends with
Select
text, it shouldn't contain it, as described in the llama 2 github issue
- Check if you wrote the model name correctly as
-
If you did everything correct, wait for the progress finishes. It will download ~16GB of files.
-
If you have issues with downloading process, check out the links above and click on the collapsible paragraph above to expand.
-
If everyting went well, you will have a directory (in our example it's named
llama-models
)llama-models (parent directory) |-download.sh |-LICENSE |-USE_POLICY.md |-Meta-Llama-3.1-8B-Instruct | |-consolidated.00.pth # our model weights file | |-params.json # our model arguments file | |-tokenizer.model # our tokenizer model file
-
Clone this repo and enter into this directory,
-
You will see the empty directory named
models-original
,llama-nuts-and-bolts (parent directory) |-... |-models-original | |-.gitkeep |-...
-
Move the directory named
llama-models/Meta-Llama-3.1-8B-Instruct
intollama-nuts-and-bolts/models-original/
directory (case sensitive). -
You should have the following directory structure:
llama-nuts-and-bolts (parent directory) |-... |-models-original | |-Meta-Llama-3.1-8B-Instruct | | |-consolidated.00.pth | | |-tokenizer.model | | |-params.json | |-.gitkeep |-...
Alongide you can run this project in a dockerized or virtualized environment, it's more suggested that to run this project without virtualization. You will see the performance difference between them.
The most performant option is to build the project and to run the executable.
You can build the project for your system's operating system, the executable will be named as llama-nb
:
# For Linux/MacOS, use:
$ go build -o llama-nb cmd/main.go
# For Windows, use:
$ go build -o llama-nb.exe cmd/main.go
Or, you can build the project for every supported platforms into the ./output
directory (you can check out the contents of the scripts/build.sh file). It will build the project for darwin (MacOS), linux, and windows platforms, 386, amd64 (Intel x64), and arm64 (64 bit ARM architecture including Apple Silicon M1/M2/M3).
$ cd llama-nuts-and-bolts
$ ./scripts/build.sh
# Check out the "output" directory in the project directory
Or, you can run the project via:
$ cd llama-nuts-and-bolts
$ go run cmd/main.go
On Linux or WSL2 on Windows, you can use ./docker-compose.yml as
$ docker-compose up -d
This command initiates a container named llama-nuts-and-bolts-cli-1
in daemon mode in idle mode.
You can run the project via:
$ docker exec -it llama-nuts-and-bolts-cli-1 go run cmd/main.go
Or, you can build the project for Linux (the container's OS), the executable will be named as llama-nb
:
$ docker exec -it llama-nuts-and-bolts-cli-1 go build -o llama-nb cmd/main.go
Or, you can build the project for every supported platforms into the ./output
directory (you can check out the contents of the scripts/build.sh file). It will build the project for darwin (MacOS), linux, and windows platforms, 386, amd64 (Intel x64), and arm64 (64 bit ARM architecture including Apple Silicon M1/M2/M3).
$ docker exec -it llama-nuts-and-bolts-cli-1 ./scripts/build.sh
# Check out the "output" directory in the project directory
Docker Desktop on Windows supports two modes: WSL 2 Mode (Windows Subsystem for Linux) and Windows virtualization mode. If you want to run second, you can use the docker/windows-amd64/Dockerfile and the docker/windows-amd64/docker-compose.yml.
You can use ./docker-compose.yml with Docker Compose and create a development environment in the container via VSCode. For more information, you can check out my other project WebRTC Nuts and Bolts's Readme chapter: Development Mode: VS Code Remote - Containers.
I've been using VS Code Remote Containers while developing this project and most of my other projects.
Run the project with executing go run ...
command or executing the compiled executable. It's more suggested that to run this project's executable after building it, and without virtualization for higher performance.
When you run the project, you will see the following screen. It prints the summary of the loading process of model files and a summary of model details.
If you select the first item in the menu by pressing 0 key and ENTER, the application prints the metadata of Llama 3.1 8B-Instruct model on the console:
Alongside you can select one of predefined prompts in the menu, you can select one of latest two items (Other, manual input
) to input your custom prompts.
With the [Text completion]
choices, the model is used only to perform text completion task. New tokens will be generated according to the input prompt text.
With the [Chat mode]
choices, the application starts the prompt with <|begin_of_text|>
string to specify "this is an instruction prompt". Also it surrounds the system prompt part with <|start_header_id|>system<|end_header_id|>\n
and <|eot_id|>
strings to specify this part is a system prompt, surrounds the user prompt part with <|start_header_id|>user<|end_header_id|>\n
and <|eot_id|>
strings to specify this part is a user prompt.
At the end, a chat mode prompt string will be look like following:
"<|begin_of_text|><|start_header_id|>system<|end_header_id|>
Always answer with emojis<|eot_id|><|start_header_id|>user<|end_header_id|>
How to go from Beijing to NY?<|eot_id|><|start_header_id|>assistant<|end_header_id|>
"
And the output of this prompt is like the following (consists of emojis with their names and unicode escape sequences):
The full-compliant, generic, production-ready, and battle-tested tensor frameworks should have support for a wide range of platforms, acceleration devices/processors/platforms, use cases, and lots of convertibility between data types, etc.
In Llama Nuts and Bolts scenario, some assumptions have been made to focus only on required set of details.
Full-compliant applications/frameworks | Llama Nuts and Bolts |
---|---|
Use existing robust libraries to read/write file formats, perform calculations, etc. | This project aims to reinvent the wheel, so it doesn't use any existing library. It implements everything it requires, precisely as much as necessary. |
Should support a wide range of different data types and perform calculations between different typed tensors in an optimized and performant way. | Has a limited elasticity for only required operations. |
Should support a wide range of different file formats. | Has a limited support for only required file formats with only required instructions. |
Should support top-k, top-p, and, temperature concepts of the LLMs (Large Language Models) to randomize the outputs, explained here. | This project doesn't have support for randomized outputs intentionally, just gives the outputs that have the highest probability. |
Should support different acceleration technologies such as nVidia CUDA, OpenCL, Metal Framework, AVX2 instructions, and ARM Neon instructions, that enable us GPGPU or SIMD (Single instruction, multiple data) usage. | This project doesn't have support for GPGPU and SIMD (Single instruction, multiple data) intentionally because it doesn't aim to be a production application, for now. However, for a few days, I had tried an experiment with ARM Neon instructions on my MacBook Pro M1, it worked successfully with float32 data type, but with the CPU cycles required to convert BFloat16 to float32 negated the saved time that came with ARM Neon. Also, I've realized that the Go compiler doesn't have support for 2-byte floats, even though I've tried using CGO. So, I gave up on this issue. If you're curious about it, you can check out the single commit on the experiment branch arm_neon_experiment. |
You are welcome to create issues to report any bugs or problems you encounter. At present, I'm not sure whether this project should be expanded to cover more concepts or not. Only time will tell 😊.
If you liked and found my project helpful and valuable, I would greatly appreciate it if you could give the repo a star ⭐ on GitHub. Your support and feedback not only help the project improve and grow but also contribute to reaching a wider audience within the community. Additionally, it motivates me to create even more innovative projects in the future.
I want to thank to contributors of the awesome sources which were referred during development of this project and writing this documentation. You can find these sources below, also in between the lines in code and documentation.
You can find a complete and categorized list of refereces in 19. REFERENCES chapter of the Llama Nuts and Bolts documentation (GitHub Pages | GitHub).
The following resources are most crucial ones, but it's suggested that to check out the 19. REFERENCES chapter:
- Meta Llama website
- Original Llama 3.1 Python package repository of Meta
- Original Llama Toolchain Python repository of Meta
- Georgi Gerganov's llama.cpp
- Wikipedia
- PyTorch Documentation
- Youtube - Andrej Karpathy - Let's build GPT: from scratch, in code, spelled out.
- The Llama 3 Herd of Models
- Llama 2: Open Foundation and Fine-Tuned Chat Models
- Llama: Open and Efficient Foundation Language Models
- Attention Is All You Need
- Youtube - Umar Jamil - Llama explained: KV-Cache, Rotary Positional Embedding, RMS Norm, Grouped Query Attention, SwiGLU
- Youtube - DeepLearning Hero - RoPE (Rotary positional embeddings) explained: The positional workhorse of modern LLMs
- Youtube - Intermation - Computer Organization and Design Fundamentals - Ep 020: Unicode Code Points and UTF-8 Encoding
- Several documents, articles, and code samples: In the code and documentation of this project, you can find several code or document links that were cited.
Llama Nuts and Bolts is licensed under the Apache License, Version 2.0. See LICENSE for the full license text.