60行代码就能构建GPT!网友:比之前的教程都要清晰|附代码

Pine 发自 凹非寺
量子位 | 公众号 QbitAI

现在只用60行代码,就能从0构建GPT了!

24be1a696798a3642e2e58a98628c3ca.png

想当初,前特斯拉前AI总监的minGPT和nanoGPT也都还要300行代码。

这个60行代码的GPT也有名字,博主将它命名为PicoGPT

不过和此前minGPT和nanoGPT的教程不同,今天要讲的这个博主的教程,更侧重于代码实现部分,模型的权重则用已经训练好的。

对此,博主解释称这篇教程的重点在于提供一个简单且易于破解的完整技术介绍

这对还不理解GPT背后概念的盆友,算是非常友好了。

ddd7dd8d4ea9aca23199f67084955fd4.png

还有网友称赞,这篇博客介绍得非常清晰,第一部分尤为如此。

这篇介绍GPT模型的文章太好了,它比我之前看到的介绍都要清晰,至少在第一部分讨论文本生成和取样是这样的。

f4b251a48b4ed0713d58cddec0b0cb77.png

目前,此项目在GitHub上标星已破百,HackerNews上的点击量也即将破千。

28273c79767ad4fa14e279502f1b940d.png

从GPT是什么讲起

在介绍之前,还是需要说明一下,这篇教程不是完全零门槛,需要读者提前熟悉Python、NumPy以及一些基本的训练神经网络。

教程的重点聚焦在技术介绍上,统共有六大部分:

什么是GPT?

按照惯例,在正式构建GPT之前得先对它做一些基本介绍,教程从输入/输出、生成文本以及训练三个部分分别来讲GPT是如何工作的。

da226f81385b40ee4720079a97e63c75.png

在这趴,博主附上代码,甚至还用了一些比喻来让读者们更好地理解GPT。

举个栗子4b3f6dec542a48e26ffc410f9f702add.png,在输入这一部分,作者将句子比作一条绳子,tokenizer则会将其分割成一小段一小段(单词),被称作token。

又比如说,在生成文本这part介绍自动回归时,博主直接贴上代码:

def generate(inputs, n_tokens_to_generate):
    for _ in range(n_tokens_to_generate): # auto-regressive decode loop
        output = gpt(inputs) # model forward pass
        next_id = np.argmax(output[-1]) # greedy sampling
        inputs = np.append(out, [next_id]) # append prediction to input
    return list(inputs[len(inputs) - n_tokens_to_generate :])  # only return generated ids

input_ids = [1, 0] # "not" "all"
output_ids = generate(input_ids, 3) # output_ids = [2, 4, 6]
output_tokens = [vocab[i] for i in output_ids] # "heroes" "wear" "capes"

在每次迭代中,它会将预测的token追加回输入,这个预测未来值并将其添加回输入的过程就是GPT被描述为自动回归的原因。

60行代码怎么运行?

了解完GPT的基本概念之后,就直接快进到了如何在电脑上运行这个PicoGPT。

博主先是甩出了他那只有60行的代码:

import numpy as np


def gpt2(inputs, wte, wpe, blocks, ln_f, n_head):
    pass # TODO: implement this


def generate(inputs, params, n_head, n_tokens_to_generate):
    from tqdm import tqdm

    for _ in tqdm(range(n_tokens_to_generate), "generating"):  # auto-regressive decode loop
        logits = gpt2(inputs, **params, n_head=n_head)  # model forward pass
        next_id = np.argmax(logits[-1])  # greedy sampling
        inputs = np.append(inputs, [next_id])  # append prediction to input

    return list(inputs[len(inputs) - n_tokens_to_generate :])  # only return generated ids


def main(prompt: str, n_tokens_to_generate: int = 40, model_size: str = "124M", models_dir: str = "models"):
    from utils import load_encoder_hparams_and_params

    # load encoder, hparams, and params from the released open-ai gpt-2 files
    encoder, hparams, params = load_encoder_hparams_and_params(model_size, models_dir)

    # encode the input string using the BPE tokenizer
    input_ids = encoder.encode(prompt)

    # make sure we are not surpassing the max sequence length of our model
    assert len(input_ids) + n_tokens_to_generate < hparams["n_ctx"]

    # generate output ids
    output_ids = generate(input_ids, params, hparams["n_head"], n_tokens_to_generate)

    # decode the ids back into a string
    output_text = encoder.decode(output_ids)

    return output_text


if name == "__main__":
    import fire

    fire.Fire(main)

然后从克隆存储库,安装依赖项等步骤一步步教你如何在电脑上运行GPT。

其中,还不乏一些贴心的小tips,比如说如果使用的是M1 Macbook,那在运行pip install之前,需要将requments.txt中的tensorflow更改为tensorflow-macos。

此外,对于代码的四个部分:gpt2,generate,main以及fire.Fire(main),博主也有做详细解释。

等到代码能够运行之后,下一步博主就准备详细介绍编码器、超参数(hparams)以及参数(params)这三部分了。

f317ff99324948159c2188b97fb8d892.png

直接在笔记本或者Python会话中运行下面这个代码:

from utils import load_encoder_hparams_and_params
encoder, hparams, params = load_encoder_hparams_and_params("124M", "models")

Bingo!一些必要的模型和tokenizer文件就直接下载到model/124M,编码器、hparams和params也能直接加载。

更具体的内容这里就不多说了,教程的链接已经附在文末。

一些基础神经网络层的介绍

这一趴涉及到的知识就更加基础了,因为下一趴是实际GPT自身的架构,所以在此之前,需要了解一些非特定于GPT的更基本的神经网络层

博主介绍了GeLU、Softmax函数以及Layer Normalization和Linear。

13995d08d4902ba608a9988b63ce766b.png

GPT架构

终于!这部分要来讲GPT自身的架构了,博主从transformer的架构引入。

be0e17115e7122a70dc287df5b8377bb.png

transformer架构

GPT的架构只使用了transformer中的解码器堆栈(即图表的右边部分),并且其中的的“交叉注意”层也没有用到。

15ab931e138b99c25af9353f26b8b0bf.png

GPT架构

随后,博主将GPT的架构总结成了三大部分:

  • 文本 + 位置嵌入

  • 变压器解码器堆栈

  • 下一个token预测头

并且还将这三部分用代码展示了出来,是酱紫的:

def gpt2(inputs, wte, wpe, blocks, ln_f, n_head):  # [n_seq] -> [n_seq, n_vocab]
    # token + positional embeddings
    x = wte[inputs] + wpe[range(len(inputs))]  # [n_seq] -> [n_seq, n_embd]

    # forward pass through n_layer transformer blocks
    for block in blocks:
        x = transformer_block(x, block, n_head=n_head)  # [n_seq, n_embd] -> [n_seq, n_embd]

    # projection to vocab
    x = layer_norm(x, ln_f)  # [n_seq, n_embd] -> [n_seq, n_embd]
    return x @ wte.T  # [n_seq, n_embd] -> [n_seq, n_vocab]

再后面,就是关于这三部分的更多细节……

测试构建的GPT

这部分将全部的代码组合在一起,就得到了gpt2.py,统共有120行代码,删除注释和空格的话,就是60行。

然后测试一下!

python gpt2.py \
    "Alan Turing theorized that computers would one day become" \
    --n_tokens_to_generate 8

结果是这样的:

the most powerful machines on the planet.

成功了!

一些后续补充

最后一部分,博主也总结了这短短60行代码的不足:非常低效!

不过他还是给出了两个可以让GPT变高效的方法:

  • 同时地而不是顺序地执行注意力计算。

  • 实现 KV 缓存。

此外,博主还推荐了一些训练模型、评估模型以及改进架构的方法和教程。

感兴趣的话,直接戳文末链接~

作者介绍

Jay Mody,目前在加拿大一家NLP初创公司Cohere从事机器学习的工作,此前,他还分别在特斯拉和亚马逊作为软件工程师实习过一段时间。

31f5978958266596d6082f467e7d33de.png

除了这篇教程之外,小哥的博客网站上还有更新其他文章,并且都有附代码~

1d05adf9d40067a791afa4d879272e40.png

代码传送门:
https://github.com/jaymody/picoGPT/blob/29e78cc52b58ed2c1c483ffea2eb46ff6bdec785/gpt2_pico.py#L3-L58
教程链接:
https://jaykmody.com/blog/gpt-from-scratch/#putting-it-all-together

《中国AIGC产业全景报告暨AIGC 50》调研启动

谁会是中国的“ChatGPT”?最有竞争力和潜力的AIGC力量位于何方?

量子位《中国AIGC产业全景报暨AIGC 50》正式启动对外征集,期待有更多优秀的机构、产品、案例与技术能够被大众看到。

b8a4c50ae50efa889e0d00f220b7061f.png

点这里👇关注我,记得标星哦~

  • 25
    点赞
  • 183
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值