llama2.c|纯C语言推理开源语言模型Baby LLaMA
你是否曾经想过在纯C语言中推理一个baby Llama 2模型?没有?现在你可以了!llama2.c|Baby LLaMA
使用这段代码,你可以在PyTorch中从头开始训练Llama 2 LLM架构,然后将权重保存为原始二进制文件,再将其加载到一个简单的500行C文件(run.c)中,用于推理模型,目前仅支持fp32。在我的云Linux开发机上,一个dim 288 6层6头模型(约15M个参数)在fp32下的推理速度约为100 tok/s,在我的M1 MacBook Air上也是差不多。我对于可以用如此简单的方法以高交互速率运行相对规模较小的模型(数千万个参数)感到相当惊讶。
请注意,这只是一个周末项目:使用nanoGPT进行了调整,实现了Llama-2架构而不是GPT-2,并且主要工作是在run.c中编写C语言推理引擎。因此,目前这不是一个真正意义上的生产级库。
向llama.cpp致敬,它启发了这个项目。我想要一个超级简单的解决方案,所以我选择硬编码llama-2架构,坚持使用fp32,并且只使用纯C语言编写一个推理文件,没有任何依赖项。
项目仓库
GitHub:karpathy/llama2.c: Inference Llama 2 in one file of pure C (github.com)
环境部署
模型下载
如果你无法通过给出的代码在线下载,也可以通过本站上传到网盘的模型下载,然后移动至项目根目录下即可。
Windows
在执行项目安装之前,我们还需要安装Git
和GCC编译器
,请先根据本站所给出的教程安装。
Windows系统安装Git
请参阅此文章:
Windows系统安装GCC编译器
请参阅此文章:
检测当前用户文档
下是否存在openai.wiki
文件夹,没有则自动创建该文件夹。
if not exist D:\openai.wiki mkdir D:\openai.wiki
通过该命令切换至该目录,方便执行后续操作。
cd /d D:\openai.wiki
执行如下命令,将该仓库远程文件拉取至本地。
git clone https://github.com/karpathy/llama2.c.git
执行如下命令,使命令行切换至克隆到本地的llama2.c库内。
cd /d D:\openai.wiki\llama2.c
执行如下命令,将会自动下载model.bin
模型至llama2.c文件夹
内。
curl -o D:\openai.wiki\llama2.c\model.bin https://karpathy.ai/llama2c/model.bin
执行如上命令之后,将会在命令行窗口内看到如下输出,待其到100%即下载完成。
D:\openai.wiki\llama2.c>curl -o D:\openai.wiki\llama2.c\model.bin https://karpathy.ai/llama2c/model.bin % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 57.9M 100 57.9M 0 0 1666k 0 0:00:35 0:00:35 --:--:-- 2753k
执行如下命令,将会自动下载model44m.bin
模型至llama2.c文件夹
内。
curl -o D:\openai.wiki\llama2.c\model44m.bin https://karpathy.ai/llama2c/model44m.bin
执行如上命令之后,将会在命令行窗口内看到如下输出,待其到100%即下载完成。
D:\openai.wiki\llama2.c>curl -o D:\openai.wiki\llama2.c\model44m.bin https://karpathy.ai/llama2c/model44m.bin % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 159M 100 159M 0 0 1538k 0 0:01:46 0:01:46 --:--:-- 1752k
在您下载完成模型之后,我们此时可以让该模型跑起来啦。在终端中执行如下命令,将会自动在项目文件夹内创建一个名为run
的文件。
gcc -O3 -o run run.c -lm
好啦,关于Windows环境的安装和编译部分已经结束,下面请看推理部分。
MacOS
在执行项目安装之前,我们还需要安装HomeBrew
环境,请先根据本站所给出的教程安装。
MacOS系统安装HomeBrew
请参阅此文章:
检测当前用户文档
下是否存在openai.wiki
文件夹,没有则自动创建该文件夹。
if [ ! -d ~/openai.wiki ]; then mkdir ~/openai.wiki fi
通过该命令切换至该目录,方便执行后续操作。
cd ~/openai.wiki
执行如下命令,将该仓库远程文件拉取至本地。
git clone https://github.com/karpathy/llama2.c.git
执行如下命令,使命令行切换至克隆到本地的llama2.c库内。
cd llama2.c
执行如下命令,将会自动下载model.bin
模型至llama2.c文件夹
内。
curl -o ~/openai.wiki/llama2.c/model.bin https://karpathy.ai/llama2c/model.bin
执行如上命令之后,将会在命令行窗口内看到如下输出,待其到100%即下载完成。
yufeng@LYF_MacBook llama2.c % curl -o ~/openai.wiki/llama2.c/model.bin https://karpathy.ai/llama2c/model.bin % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 57.9M 100 57.9M 0 0 11.3M 0 0:00:05 0:00:05 --:--:-- 13.5M
执行如下命令,将会自动下载model44m.bin
模型至llama2.c文件夹
内。
curl -o ~/openai.wiki/llama2.c/model44m.bin https://karpathy.ai/llama2c/model44m.bin
执行如上命令之后,将会在命令行窗口内看到如下输出,待其到100%即下载完成。
yufeng@LYF_MacBook llama2.c % curl -o ~/openai.wiki/llama2.c/out44m.bin https://karpathy.ai/llama2c/model44m.bin % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 159M 100 159M 0 0 8949k 0 0:00:18 0:00:18 --:--:-- 15.9M
在您下载完成模型之后,我们此时可以让该模型跑起来啦。在终端中执行如下命令,将会自动在项目文件夹内创建一个名为run
的文件。
gcc -O3 -o run run.c -lm
好啦,关于MacOS环境的安装和编译部分已经结束,下面请看推理部分。
模型推理
推理的意思就是生成文本,此时我们共计拥有两个模型,名为model.bin
的模型为作者的第一个Demo模型,后续出了优化版本的model44m.bin
模型。
推荐使用model44m.bin
模型,可以拥有更好的推理效果。但站长把两个都推理方法都告诉大家吧。
model.bin
Windows:如果使用名为model.bin
的模型进行推理,那么我们执行如下代码即可。
run.exe D:\openai.wiki\llama2.c\model.bin
MacOS:如果使用名为model.bin
的模型进行推理,那么我们执行如下代码即可。
./run ~/openai.wiki/llama2.c/model.bin
输出内容如下:
One day, a blue bird named Bob lived in a small house. He loved motorcycles. He had a friend named Tim. Tim was a small boy who lived with Bob. They played and laughed together all day. One morning, Bob said, "Tim, I have an idea! Let's invite our friends to play in my house!" Tim was very happy. They invited their friends, but they wanted to play with the blue motorcycle too. On the day of the party, all their friends came. They all had fun pretending to ride the blue motorcycle. They played games and sang songs. But then, something unexpected happened. The blue motorcycle started to move! Bob and Tim were scared. They didn't know what to do. But then, the blue motorcycle took off into the sky and flew them back to their house. They were safe and happy again. The end. <s> Once upon a time, there was a big dog named Max. Max loved to play in the park with his friends. One day, Max saw a little boy crying. The little boy had lost his toy car and was very sad. Max felt compassionate and wanted to help the little boy. achieved tok/s: 108.709961
以下是中文翻译:
从前,有只叫鲍勃的蓝色鸟住在一座小房子里。他喜欢摩托车。他有一个名叫蒂姆的朋友。蒂姆是个小男孩,和鲍勃一起住在房子里。他们整天一起玩耍和笑闹。 一天早上,鲍勃说:“蒂姆,我有个主意!让我们邀请我们的朋友来我家玩!”蒂姆非常高兴。他们邀请了朋友,但他们也想玩蓝色的摩托车。 在派对的日子,所有的朋友都来了。他们都在假装骑蓝色的摩托车时玩得很开心。他们玩游戏,唱歌。但突然,发生了意想不到的事情。蓝色的摩托车开始动了! 鲍勃和蒂姆吓坏了。他们不知道该怎么办。但是,蓝色的摩托车飞上天空,把他们带回了家。他们又安全又开心。故事结束。 从前,有只叫马克斯的大狗。马克斯喜欢和朋友们在公园里玩耍。有一天,马克斯看到一个小男孩在哭泣。小男孩丢了他的玩具车,很伤心。马克斯感到同情,想帮助这个小男孩。 完成的tok/s速度:108.709961
model44m.bin
Windows:如果使用名为model44m.bin
的模型进行推理,那么我们执行如下代码即可。
run.exe D:\openai.wiki\llama2.c\model44m.bin
MacOS:如果使用名为model44m.bin
的模型进行推理,那么我们执行如下代码即可。
./run ~/openai.wiki/llama2.c/model44m.bin
输出内容如下:
One day, a little girl named Lucy went to the park. She saw a purple bird in a tree. The bird was very pretty. Lucy wanted to play with the bird, so she called out, "Hi, pretty bird! Come play with me!" The bird came down and lay on the ground. It was very slow. Lucy picked up the bird and put it in her pocket. She thought it would be fun to take the bird home. When Lucy got home, she showed her mom the purple bird. But her mom said, "Lucy, that is not a real bird. It is a toy bird!" Lucy was very sad. Her mom took the toy bird away and they went back to the park. Lucy knew she could not keep the purple bird, and she went home with a sad face. <s> One day, a little girl named Lucy wanted to play with her toys. She had a toy bunny, a toy car, and a toy bear. Lucy was so excited to play with her toys and have fun. Lucy's mom was in the kitchen, listening to a noisy sound. She could feel the toys being connected in a fun quiz game that Lucy had made. Lucy was very happy and started to play the quiz game with her mom. As they played the quiz game, Lucy's mom told her to shake the toy bunny. Lucy shook the bunny, and all three of them laughed together. They played the quiz game all day, and it was a very fun day for Lucy and her mom. <s> Once upon a time, there was a little bird named Pip. Pip was very lonely because he didn't have any friends to play with. One day, Pip saw a big wire in the sky and he thought it would make a good place to sing and fly. So, he flew up to the wire and sat there. As Pip started to sing, a big cat named Max came along and saw him. Max said, "Hello little bird, why are you here? You look lonely." Pip replied, "I'm lonely because I don't have any friends to sing with." Max thought for a moment and said, "I'll be your friend and we can sing together." Pip was very happy and they both sang together all day long. As they were singing, a big gust of wind came and the wire started to move. Pip and Max were scared and didn't know what to do. Just then, they saw a bird named Polly flying towards them. Polly said, "Don't be scared, I'm here to help you." Bob and Max were surprised but happy to have a new friend. And so, Pip, Max, and Polly sang together and flew around the wire, having lots of fun. From that day on, Pip was never lonely again because he had friends to sing with. <s> Once upon a time, there was a boy named Tim. Tim had a blue toy boat that he loved to sail. One day, Tim wanted to sail his boat on the big water. He was very excited and could not wait to play. Tim's mom told him to wait for his dad to come home from work. Tim was impatient. He wanted to go now, and so he decided to sail his boat in the big water near his house. He thought it would be fun to see his boat sail higher and higher. As Tim sailed his boat, a big fish came and took it away. Tim was very sad. He learned that he should have waited for his dad. The moral of the story is to be patient and wait for the right time before doing something new. <s> Once upon a time, there was a little girl named Lily. She loved to play in the park with her friends. One day, Lily and her friends saw a big tree with a lot of honey on it. They wanted to taste the honey, but the tree was too tall. Suddenly, they saw a bee who was also trying to get the honey. The bee said, "I'm the strongest bee, and I can make the honey too." Lily and her friends said, "That's not nice, bee." The bee didn't listen and tried to make the honey anyway. But the tree shook and the bees got angry. They started to fly around Lily and her friends. Lily said, "Let's spread some flowers on the ground, so the bees won't fly into us." They found some flowers and spread them all over the tree. The bees liked the flowers and flew away. Lily and her friends were happy they could enjoy achieved tok/s: 28.724574
以下是中文翻译:
从前,有个小女孩叫露西。一天,她去了公园,看见一只紫色的鸟在树上。这只鸟非常漂亮。露西想和鸟儿一起玩,于是她喊道:“嗨,漂亮的鸟儿!来和我玩吧!” 鸟儿飞下来,躺在地上。它动作很慢。露西把鸟儿捡起来放进口袋里。她觉得带着这只鸟儿回家会很有趣。 回到家后,露西向妈妈展示了这只紫色的鸟儿。但她妈妈说:“露西,那不是一只真的鸟儿。那是个玩具鸟!”露西很伤心。她的妈妈把玩具鸟拿走了,他们又回到了公园。露西知道她不能留下这只紫色的鸟,于是她带着悲伤的面孔回家了。 一天,小女孩露西想要和她的玩具一起玩。她有一只玩具兔子,一辆玩具车和一只玩具熊。露西很兴奋地准备和她的玩具一起玩,享受快乐的时光。 露西的妈妈在厨房听到了一个有趣的声音。她发现露西用玩具做了一个有趣的问答游戏。露西非常高兴,开始和妈妈一起玩问答游戏。 在他们玩问答游戏的时候,露西的妈妈告诉她摇动玩具兔子。露西摇了摇兔子,三个人一起笑了起来。他们整天都在玩问答游戏,对露西和她的妈妈来说,是非常快乐的一天。 从前,有只小鸟叫皮普。皮普非常孤独,因为他没有朋友一起玩。有一天,皮普看到天上有一根大线,他觉得那是一个很好的地方,可以在那里唱歌和飞翔。于是,他飞上了线,并坐在那里。 当皮普开始唱歌时,一只大猫叫马克斯走过来看到了他。马克斯说:“你好小鸟,你为什么在这里?你看起来很孤单。”皮普回答:“我很孤单,因为我没有朋友一起唱歌。”马克斯思考了一会儿说:“我愿意成为你的朋友,我们一起唱歌吧。” 皮普非常高兴,他们一起唱了整天。当他们唱歌时,一阵大风吹来,线开始晃动。皮普和马克斯吓坏了,不知道该怎么办。就在那时,他们看到一只叫波利的小鸟飞向他们。波利说:“别害怕,我来帮助你们。”皮普和马克斯感到惊讶,但很高兴有了一个新朋友。 于是,皮普、马克斯和波利一起唱歌,在线的周围飞翔,玩得很开心。从那天起,皮普再也不孤单了,因为他有朋友一起唱歌。 从前,有个叫蒂姆的男孩。蒂姆有一只蓝色的玩具船,他非常喜欢在水里放船。有一天,蒂姆想要在大水里放他的船。他非常兴奋,迫不及待地想要玩耍。 蒂姆的妈妈告诉他等爸爸下班后再出去放船。蒂姆不耐烦了。他想现在就去,于是他决定在离家不远的大水里放他的船。他觉得看到船在水面上越来越高会很有趣。 蒂姆放船时,一条大 鱼游过来把船带走了。蒂姆很伤心。他明白了应该等待他的爸爸。这个故事的寓意是在做新事情之前要有耐心等待适当的时机。 从前,有个小女孩叫莉莉。她喜欢和朋友在公园里玩耍。有一天,莉莉和她的朋友们看见一棵有很多蜜蜂蜜的大树。他们想尝一下蜜蜂蜜,但是树太高了。 突然,他们看见一只蜜蜂也想要得到蜜。蜜蜂说:“我是最强壮的蜜蜂,我也可以做蜜蜂蜜。”莉莉和她的朋友们说:“蜜蜂,这样不好。” 蜜蜂不听,还是试图做蜜蜂蜜。但树摇晃了,蜜蜂们生气了,开始飞来飞去围着莉莉和她的朋友们。莉莉说:“我们把一些花撒在地上,这样蜜蜂就不会飞到我们身上。”他们找到一些花撒在树下。蜜蜂喜欢这些花,飞走了。莉莉和她的朋友们很高兴,可以享受了 完成的tok/s速度:28.724574
模型训练
资源下载
如果你无法通过给出的代码在线下载,也可以通过本站上传到网盘的模型下载,然后移动至项目中的out目录下即可。
训练示例
让我们在C语言中运行一个baby Llama 2
模型。你需要一个模型。可以下载TinyStories
数据集上训练的这个1500万参数的模型(约58MB),并将其放置在默认的模型目录out
中:
wget https://karpathy.ai/llama2c/model.bin -P out
如果无法下载,可以直接下载国内网盘所提供的model.bin
模型,将其放置在out
目录内。
终端中执行如下代码,编译并运行C代码:
gcc -O3 -o run run.c -lm ./run out/model.bin
你将看到文本流动地输出一个样本。在我的M1 MacBook Air上,这个运行速度约为100个令牌/秒,对于超级幼稚的fp32单线程C代码来说已经不错了。查看性能部分,了解能够显著提高速度的编译标志。样本输出如下:
从前,有一个叫Timmy的男孩。Timmy喜欢和朋友们一起玩运动。他非常擅长投掷和接住球。有一天,Timmy的妈妈给了他一件新的衬衫,让他去参加派对。Timmy觉得很棒,于是问妈妈衬衫有什么用途。妈妈说:“衬衫就像篮球比赛的特殊服装。”Timmy听到后很开心,他穿上了新的衬衫。他觉得自己像士兵去参军一样大声呐喊。从那天开始,每次Timmy和朋友们在派对上一起玩运动时,他都会穿上新的衬衫。从前,有一个叫Lily的小女孩。她喜欢和朋友们在外面玩。有一天,Lily和她的朋友Emma在玩球。Emma把球扔得太用力了,结果打到了Lily的脸上。Lily感到尴尬,不想再玩了。Emma问Lily怎么了,Lily告诉她发生的事情。Emma告诉Lily,她感到尴尬是因为她把球扔得太用力了。Lily感到难过……(以下部分省略,数据性能信息)
更新:我现在也上传了一个更大的模型。这个模型的维度是512,有8层,8个头,上下文长度为1024,是一个约44M个参数的Transformer模型。它在4个XA100 40GB GPU上训练了200K次,批次大小为32,耗时约8小时。你可以像下面这样使用这个更大、更强大的模型:
wget https://karpathy.ai/llama2c/model44m.bin -P out44m ./run out44m/model44m.bin
在我的MacBook Air上,使用$ gcc -Ofast -o run run.c -lm
编译后,运行速度约为150个令牌/秒。这还是太快了!我必须训练一个更大的模型……这个模型生成的故事更加连贯多样:
从前从前,有一个叫莉莉的小女孩。她喜欢在床上和她的玩具一起玩。一天,她决定和她的玩具做个茶话会。她把一些茶倒进一个小茶壶里,放在茶杯上。突然,她的小弟弟马克斯走进房间,也想加入茶话会。莉莉不想分享她的茶,她告诉马克斯走开。马克斯开始哭,莉莉感到难过。她决定把茶话会让给马克斯,他们一起分享了茶壶。但接着,发生了一件意想不到的事情。茶壶开始摇晃和颤动。莉莉和马克斯感到害怕,不知道该怎么办。突然,茶壶开始飞向天花板,落在床上。莉莉和马克斯感到惊讶,并拥抱在一起。他们意识到分享比自私更有趣。从那天起,他们总是分享他们的茶话会和玩具。
使用指南
您可以加载Meta发布的权重,但由于这个基于单线程C程序的Baby推理速度,即使是7B模型的推理速度,可能也不会很好。因此,在这个存储库中,我们专注于更狭义的应用,并从头开始训练相同的架构,比如在TinyStories数据集上进行有趣的训练。
首先,让我们下载并预处理一些源数据集,例如,我喜欢TinyStories,因此它是当前在此存储库中唯一可用的示例。但是添加数据集应该非常容易,参见代码。
python tinystories.py download python tinystories.py pretokenize
然后训练我们的模型:
python train.py
有关更多奇特的启动和超参数覆盖,请参见train.py脚本。我没有调整超参数,我预计简单的超参数探索应该可以得到更好的模型。如果您希望跳过模型训练,完全可以理解,对于简单的演示,只需下载我的预训练模型并将其保存到out
目录即可。
wget https://karpathy.ai/llama2c/model.bin -P out
得到model.bin
文件后,我们可以在C语言中进行推理。首先编译C代码:
gcc -O3 -o run run.c -lm
然后你可以简单地运行它:
./run out/model.bin
令牌会流动显示,非常有趣!我们也可以运行PyTorch推理脚本进行比较(运行前,如果还没有将model.ckpt添加到/out
中):
python sample.py
结果将与C代码的输出相同。更详细的测试将在test_all.py
中进行,运行:
$ pytest
目前你需要两个文件进行测试或抽样:来自PyTorch训练的model.bin文件和model.ckpt文件。我需要考虑如何在不下载200MB数据的情况下运行测试。
性能优化
(注意:本指南并不完美,因为我个人大部分时间都在Python领域,并没有对很多特性和标志有深入了解。如果有人愿意帮助文档编写并简要描述其中的一些特性和权衡。)
根据您的系统,有很多潜在的方法可以加速这段代码。这里我们记录了一些方法以及它们的高级指南。这是默认的编译方式,但使用-O3:
gcc -O3 -o run run.c -lm
-O3包含一些优化,这些优化在编译时间和内存使用方面都是昂贵的。包括向量化、循环展开和预测分支。以下是一些其他尝试的方法。
-Ofas
t执行额外的优化,可能会违反C/IEEE规范,除了-O3
之外。有关更多信息,请参阅GCC文档。
-ffast-math
打破IEEE合规性,例如允许重新排序操作,禁用一些用于NaN的检查(假设它们不会发生),启用倒数逼近,禁用有符号零等。
-funsafe-math-optimizations
一种更有限的形式的-ffast-math,仍然会违反IEEE合规性,但不包含-ffasth-math中的所有数值/错误处理更改。有关更多信息,请参阅GCC文档。
-march=native
编译程序以使用您正在编译的计算机的体系结构,而不是更通用的CPU。这可能会启用其他优化和硬件特定的调整,例如改进的向量指令/宽度。
将其中几个组合在一起,我在我的MacBook Air(M1芯片)上看到的最快吞吐量是:
gcc -Ofast -o run run.c -lm
此外,我看到有人用clang
代替gcc
报告了更高的吞吐量。
未排序的待办事项
- 为什么C语言采样代码中有一个前导空格,当我们运行
./run
时? - 待办事项多查询支持?对于在CPU上运行的较小模型似乎不太有用(?)
- 待办事项支持超出
max_seq_len
步骤的推理,必须考虑kv缓存。 - 为什么在我的A100 40GB上MFU(Most Frequently Used Cache Line)如此低(约10%)?
- 在使用DDP时与torch.compile和wandb出现奇怪的错误
- 制作更好的测试以减少yolo(?)
许可证
MIT
我执行下述命令报了这个错误,是什么原因呢?
C:\openai.wiki\llama2.c>gcc -O3 -o run run.c -lm
C:\Users\alber\AppData\Local\Temp\ccljSk3F.o:run.c:(.text+0x2fdc): undefined reference to `clock_gettime’
C:\Users\alber\AppData\Local\Temp\ccljSk3F.o:run.c:(.text.startup+0x153): undefined reference to `mmap’
C:\Users\alber\AppData\Local\Temp\ccljSk3F.o:run.c:(.text.startup+0x513): undefined reference to `clock_gettime’
C:\Users\alber\AppData\Local\Temp\ccljSk3F.o:run.c:(.text.startup+0x586): undefined reference to `clock_gettime’
C:\Users\alber\AppData\Local\Temp\ccljSk3F.o:run.c:(.text.startup+0x62c): undefined reference to `munmap’
collect2.exe: error: ld returned 1 exit status
@abysslao 我也遇到了这个问题,不知道怎么处理~
我的安装也遇到同类问题:
C:\Openai.wiki>cd llama2.c
C:\Openai.wiki\llama2.c>gcc -O3 -o run run.c -lm
C:\Users\ADMINI~1\AppData\Local\Temp\2\ccTA6yIw.o:run.c:(.text+0x440): undefined reference to `mmap’
C:\Users\ADMINI~1\AppData\Local\Temp\2\ccTA6yIw.o:run.c:(.text+0x56d): undefined reference to `munmap’
C:\Users\ADMINI~1\AppData\Local\Temp\2\ccTA6yIw.o:run.c:(.text+0x3f4c): undefined reference to `clock_gettime’
C:\Users\ADMINI~1\AppData\Local\Temp\2\ccTA6yIw.o:run.c:(.text+0x4137): undefined reference to `clock_gettime’
C:\Users\ADMINI~1\AppData\Local\Temp\2\ccTA6yIw.o:run.c:(.text+0x420a): undefined reference to `clock_gettime’
collect2.exe: error: ld returned 1 exit status