当前位置:首页 >> 电热设备
电热设备

高能技巧!60 行 NumPy 代码 才行实现一个 GPT

时间:2023/05/13 12:17:24 来源:电热设备

oes", "wear"]# the decode() method converts back a list[int] -> strtext = tokenizer.decode(ids) # text = "not all heroes wear"

简而言之:

我们有一个字串

我们适用一个上标筒将其裂解变为更为小的外,叫作暂存筒(tokens)

我们适用形的单口语将这些上标射影变为幂。

在实践当中,我们适用更为技术的上标化方法,而不是直观地通过数量有限划分,例如 Byte-Pair Encoding 或 WordPiece,但原理是一样的:

有一个形的单口语,将字串上标射影为幂数据库

有一个序列方法可以将str->list[int]叠加。

有一个解密方法可以将list[int] -> str叠加。

Output

Output 是一个二维数组,其当中 output[i][j] 是静态的原始数据分析机率,即 vocab[j] 的用户端是下一个暂存筒 inputs[i+1]。例如:

vocab = ["all", "not", "heroes", "the", "wear", ".", "capes"]inputs = [1, 0, 2, 4] # "not" "all" "heroes" "wear"output = gpt(inputs)# ["all", "not", "heroes", "the", "wear", ".", "capes"]# output[0] = [0.75 0.1 0.0 0.15 0.0 0.0 0.0 ]# given just "not", the model predicts the word "all" with the highest probability# ["all", "not", "heroes", "the", "wear", ".", "capes"]# output[1] = [0.0 0.0 0.8 0.1 0.0 0.0 0.1 ]# given the sequence ["not", "all"], the model predicts the word "heroes" with the highest probability# ["all", "not", "heroes", "the", "wear", ".", "capes"]# output[-1] = [0.0 0.0 0.0 0.1 0.0 0.05 0.85 ]# given the whole sequence ["not", "all", "heroes", "wear"], the model predicts the word "capes" with the highest probability

为了给予整个脱氧核糖核酸的下一个暂存筒原始数据分析,我们必需取 output[-1] 当中机率高于的一个暂存筒 :

vocab = ["all", "not", "heroes", "the", "wear", ".", "capes"]inputs = [1, 0, 2, 4] # "not" "all" "heroes" "wear"output = gpt(inputs)next_token_id = np.argmax(output[-1]) # next_token_id = 6next_token = vocab[next_token_id] # next_token = "capes"

将机率高于的暂存筒作为我们的之后原始数据分析,通常被叫作 greedy decoding 或 greedy sampling。

原始数据分析一个脱氧核糖核酸当中的下一个范的单该词的训练任务被叫作口语可视化。因此,我们可以把 GPT 叫作口语静态。

生变为一个该词是很酷,但整个短语、段落等呢...?

生变为评节录

自复出

我们可以通过反复询问静态原始数据分析下一个暂存筒来生变为零碎的短语。在每次子程序时,我们将原始数据分析的暂存筒替换成到重定向当中:

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 idsinput_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"

这个原始数据分析先于见倍数(复出),并将其加回重定向(自动)的上次就是为什么你不太可能看着 GPT 被所述为自复出的主因。

调制

我们可以通过从机率分布当中量化而不是贪财地样,为我们的生变为引进一些并不一定(并不一定):

inputs = [1, 0, 2, 4] # "not" "all" "heroes" "wear"output = gpt(inputs)np.random.choice(np.arange(vocab_size), p=output[-1]) # capesnp.random.choice(np.arange(vocab_size), p=output[-1]) # hatsnp.random.choice(np.arange(vocab_size), p=output[-1]) # capesnp.random.choice(np.arange(vocab_size), p=output[-1]) # capesnp.random.choice(np.arange(vocab_size), p=output[-1]) # pants

它不仅并不须要我们为相异的重定向生变为并不相同的短语,而且与 greedy decoding 远比,它还提高了转换成的总质量。

在量化早先,适用 top-k、top-p 和温度等技术来变更机率分布也是很常见的。这必要性提高了生变为的总质量,并引进了;也反之亦然,我们可以通过这些反之亦然来给予并不相同的生变为言道为(例如,减小温度使我们的静态负起更为多的风险,从而更为有 "创造性")。

受训

我们像其他骨骼肌互联网一样受训 GPT,适用分量攀升法来受训一些死伤给定。在 GPT 的状况下,我们将交错总能量死伤常用口语可视化训练任务:

def lm_loss(inputs: list[int], params) -> float: # the labels y are just the input shifted 1 to the left # # inputs = [not, all, heros, wear, capes] # x = [not, all, heroes, wear] # y = [all, heroes, wear, capes] # # of course, we don't have a label for inputs[-1], so we exclude it from x # # as such, for N inputs, we have N - 1 langauge modeling example pairs x, y = inputs[:-1], inputs[1:] # forward pass # all the predicted next token probability distributions at each position output = gpt(x, params) # cross entropy loss # we take the average over all N-1 examples loss = np.mean(-np.log(output[y])) return lossdef train(texts: list[list[str]], params) -> float: for text in texts: inputs = tokenizer.encode(text) loss = lm_loss(inputs, params) gradients = compute_gradients_via_backpropagation(loss, params) params = gradient_descent_update_step(gradients, params) return params

为了吻合起见,我们在 GPT 的重定向当中去除了 params 反之亦然。在受训尿素的上次子程序当中,我们执言道分量攀升方法来更为新静态反之亦然,使我们的静态在看着每一段新的评节录时都能更为好地进言道口语可视化。这是一个并不比较简单的受训另设。

请肯定,我们没有适用确切上标的原始数据。相反,我们能够从原始评节录本身转化成重定向/ID对。这就是实际上的自我监督进修。

这将意味着我们可以更为容易地扩大受训原始数据,必需向静态展示尽不太可能多的原始评节录。例如,GPT-3 适用了来自互联网和类书籍的 3000 亿个评节录上标上进言道受训。

GPT-2 科学论文当中的列于 2.3

你须要一个足够大的静态,以便能够从所有原始数据当中进修,这就是为什么 GPT-3 有 1750 亿个反之亦然,并且不太可能须要耗费 100 万到 1000 万美元的计算变为本来受训。

这种自我监督的受训方法被叫作先于受训,因为我们可以重复适用 "先于受训 "的静态二阶,以便在北岸训练任务上必要性受训静态,例如分类学推文是否有毒。

在北岸训练任务上受训静态被叫作变更,因为静态的二阶早已被先于受训变为能够思考口语,所以它只是针对当前的具体训练任务进言道变更。

这种 "一般训练任务进言道先于受训+特定训练任务进言道变更 "的策略被叫作重新分配进修。

查看

原则上,本来的 GPT 只是关于先于受训叠加进修的产物筒静态的益一处,像是 BERT。

直到在 GPT-2 和 GPT-3 的科学论文当中,我们才认识到一个先于受训好的 GPT 静态本身能够执言道任何训练任务,必需查看它并进言道自复出口语可视化,不须要变更。这被叫作词汇进修,因为该静态只是为了让查看的词汇来执言道训练任务。词汇进修可以是零次、一次或几次。

当然,你可以将 GPT 看成是一个闲谈机筒人,而不是让它确切地花钱 "训练任务"。对话历史被作为查看传输到静态当中,无论如何才会在前面连带一些所述,如 "你是一个闲谈机筒人等"。如果你改变了查看,你甚至可以给你的闲谈机筒人一个角色。

有了这些,让我们最终来看看意味著的借助吧。

装配

生化本导论的驱动器库:

git clone cd picoGPT

装配依赖项:

pip install -r requirements.txt

请肯定,如果你适用的是 M1 Macbook,则在运言道 pip 装配早先,须要在 requirements.txt 当副司令 tensorflow 更为改回 tensorflow macos。此文档在 Python 3.9.10上 进言道了测试者。

每个元数据的快速细分:

encoder.py 构成 OpenAI 的 BPE Tokenizer 的文档;

utils.py 构成流媒体和读取 GPT-2 静态二阶、上标筒和;也反之亦然的文档;

gpt2.py 构成意味著的 GPT 静态和生变为文档,我们可以将其作为 python 脚本运言道;

gpt2pico.py 与 gpt2.py 相异,但文档言道更为少。

我们将从两头开始再次度借助 gpt2.py,因此让我们删掉它并将其再次度创建为空元数据:

rm gpt2.pytouch gpt2.py

首先,将列于列出文档折叠到 :gpt2.py

import numpy as npdef gpt2(inputs, wte, wpe, blocks, ln_f, n_head): pass # TODO: implement thisdef 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 idsdef 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_textif __name__ == "__main__": import fire fire.Fire(main)

细分为 4 个外:

1、gpt2 给定是我们必定才会借助的意味著 GPT 文档。您才会肯定到,除了重定向除此以外,给定手撰写还构成一些额外的细节:

wte、wpe、block 和 lnf 是我们静态的反之亦然。

n_head 是持续性传输长期须要的;也反之亦然。

2、该给定是我们此前探究的自复出解密算法。为了直观起见,我们适用贪财调制。tqdm 是一个进度条,为了让我们可视化解密上次,因为它一次生变为一个暂存筒。

3、main( )主给定一处理:

读取上标筒(类比)、静态二阶(反之亦然)和;也反之亦然(hparam)

适用 tokenizer 将重定向查看序列为暂存筒 ID

线程生变为给定

将转换成 ID 解密为字串

4、fire.fire(main)只是将我们的元数据叠加变为一个 CLI 系统设计程序,因此我们之后可以适用:python-gpt2.py“some prompt here”运言道文档

让我们仔细看看类比、hparam 和 params,在笔记本或交互的单 Python 才应答当中,运言道:

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

这将把必要的静态和上标筒元数据流媒体到我们的文档当中,并将类比、hparam 和 params 读取到我们的文档当中。

类比

encoder 是 GPT-2 适用的 BPE tokenizer。

> ids = encoder.encode("Not all heroes wear capes.")> ids[3673, 477, 10281, 5806, 1451, 274, 13]> encoder.decode(ids)"Not all heroes wear capes."

适用 tokenize r的名该词(驱动器在 encoder.decoder 当中),我们可以看一下意味著的暂存筒是什么。

[encoder.decoder[i] for i in ids]['Not', 'Ġall', 'Ġheroes', 'Ġwear', 'Ġcap', 'es', '.']

肯定,有时我们的暂存筒是后缀(如Not),有时是后缀但前面才会有空格(如 Ġall,Ġ 代列于空格),有时是后缀的一外(如 capes 被分变为 Ġcap 和 es),有时是标点符号(如.)。

BPE 的一个用一处是它可以对任何任意的字串进言道序列。如果它遇到了形的单口语当中没有的东西,它只是将其裂解为它所思考的长子字串:

[encoder.decoder[i] for i in encoder.encode("zjqfl")]['z', 'j', 'q', 'fl']

我们还可以检查和名该词的椭圆形:

> len(encoder.decoder)50257

当我们读取 tokenizer 时,我们即将从一些元数据当中读取早已受训好的名该词和寄存器对新设,这些元数据是在运言道 load_encoder_hparams_and_param 时与静态元数据独自流媒体。

;也反之亦然

hparams 是一个原文,构成静态的;也反之亦然:

> hparams{ "n_vocab": 50257, # number of tokens in our vocabulary "n_ctx": 1024, # maximum possible sequence length of the input "n_embd": 768, # embedding dimension (determines the "width" of the network) "n_head": 12, # number of attention heads (n_embd must be divisible by n_head) "n_layer": 12 # number of layers (determines the "depth" of the network)}

我们将在文档的原文当中适用这些符号来显示事物的原则上结构上。我们还将适用 n_seq 来列于示我们重定向脱氧核糖核酸的长度(即n_seq = len(inputs))。

反之亦然

params 是一个嵌套的 Json 原文,用来保存我们静态的受训二阶。Json 的苞节点是 NumPy 数组。如果我们打印 params,用它们的椭圆形去掉数组,我们才会获取:

import numpy as npdef shape_tree(d): if isinstance(d, np.ndarray): return list(d.shape) elif isinstance(d, list): return [shape_tree(v) for v in d] elif isinstance(d, dict): return {k: shape_tree(v) for k, v in d.items()} else: ValueError("uh oh")print(shape_tree(params)){ "wpe": [1024, 768], "wte": [50257, 768], "ln_f": {"b": [768], "g": [768]}, "blocks": [ { "attn": { "c_attn": {"b": [2304], "w": [768, 2304]}, "c_proj": {"b": [768], "w": [768, 768]}, }, "ln_1": {"b": [768], "g": [768]}, "ln_2": {"b": [768], "g": [768]}, "mlp": { "c_fc": {"b": [3072], "w": [768, 3072]}, "c_proj": {"b": [768], "w": [3072, 768]}, }, }, ... # repeat for n_layers ]}

这些都都从本来的 OpenAI tensorflow 检查和点读取的:

>>> import tensorflow as tf>>> tf_ckpt_path = tf.train.latest_checkpoint("models/124M")>>> for name, _ in tf.train.list_variables(tf_ckpt_path):>>> arr = tf.train.load_variable(tf_ckpt_path, name).squeeze()>>> print(f"{name}: {arr.shape}")model/h0/attn/c_attn/b: (2304,)model/h0/attn/c_attn/w: (768, 2304)model/h0/attn/c_proj/b: (768,)model/h0/attn/c_proj/w: (768, 768)model/h0/ln_1/b: (768,)model/h0/ln_1/g: (768,)model/h0/ln_2/b: (768,)model/h0/ln_2/g: (768,)model/h0/mlp/c_fc/b: (3072,)model/h0/mlp/c_fc/w: (768, 3072)model/h0/mlp/c_proj/b: (768,)model/h0/mlp/c_proj/w: (3072, 768)model/h1/attn/c_attn/b: (2304,)model/h1/attn/c_attn/w: (768, 2304)...model/h9/mlp/c_proj/b: (768,)model/h9/mlp/c_proj/w: (3072, 768)model/ln_f/b: (768,)model/ln_f/g: (768,)model/wpe: (1024, 768)model/wte: (50257, 768)

列于列出文档将上述 tensorflow 给定叠加为 params 原文。

作为参考,列于列出是反之亦然的椭圆形,但数字由它们所代列于的 hparams 本来:

{ "wpe": [n_ctx, n_embd], "wte": [n_vocab, n_embd], "ln_f": {"b": [n_embd], "g": [n_embd]}, "blocks": [ { "attn": { "c_attn": {"b": [3*n_embd], "w": [n_embd, 3*n_embd]}, "c_proj": {"b": [n_embd], "w": [n_embd, n_embd]}, }, "ln_1": {"b": [n_embd], "g": [n_embd]}, "ln_2": {"b": [n_embd], "g": [n_embd]}, "mlp": { "c_fc": {"b": [4*n_embd], "w": [n_embd, 4*n_embd]}, "c_proj": {"b": [n_embd], "w": [4*n_embd, n_embd]}, }, }, ... # repeat for n_layers ]}

当我们借助 GPT 时,你不太可能才会须要回来参考这个原文来检查和二阶的椭圆形。为了某种程度,我们将把文档当中的给定名与此原文的关键字进言道反之亦然。

原则上层

在我们离开意味著的 GPT 框架本身早先,让我们借助一些对 GPT 不特定的更为原则上的骨骼肌互联网层。

GELU

GPT-2 选项的非二阶(激活给定)是 GELU(克雷误差二阶单位),是 ReLU 的替代方案。

该图来自 GELU 科学论文

它与列于列出给定近似于:

def gelu(x): return 0.5 * x * (1 + np.tanh(np.sqrt(2 / np.pi) * (x + 0.044715 * x**3)))

与 ReLU 一样,GELU 对重定向类型进言道配置:

>>> gelu(np.array([[1, 2], [-2, 0.5]]))array([[ 0.84119, 1.9546 ], [-0.0454 , 0.34571]])

BERT 受到重视了 GeLU 在 transformer 静态当中的适用。

Softmax

好的 Softmax:

def softmax(x): exp_x = np.exp(x - np.max(x, axis=-1, keepdims=True)) return exp_x / np.sum(exp_x, axis=-1, keepdims=True)

我们适用 max(x) 的长一处来借助数倍数的有利于性。

Softmax 常用将除此以外实数叠加为机率(在 0 和 1 间,数字的总和为 1)。我们在重定向的最终一个轴上系统设计 softmax。

> x = softmax(np.array([[2, 100], [-5, 0]]))> xarray([[0.00034, 0.99966], [0.26894, 0.73106]])> x.sum(axis=-1)array([1., 1.])

层标准规约化

层标准规约化将倍数标准规约化为标准规约差为 0,方差为 1:

def layer_norm(x, g, b, eps: float = 1e-5): mean = np.mean(x, axis=-1, keepdims=True) variance = np.var(x, axis=-1, keepdims=True) x = (x - mean) / np.sqrt(variance + eps) # normalize x to have mean=0 and var=1 over last axis return g * x + b # scale and offset with gamma/beta params

层标准规约化能够基座的重定向无论如何在一致的仅限于,这可以延缓和有利于受训上次。与批量标准规约化一样,标准规约化转换成随后被图层,并用两个可进修乘积 gamma 和 beta 进言道对齐。个数当中的小 ε 项常用避免除以零的误差。

我们在重定向的最终一个轴上系统设计层标准规约化。

> x = np.array([[2, 2, 3], [-5, 0, 1]])> x = layer_norm(x, g=np.ones(x.shape[-1]), b=np.zeros(x.shape[-1]))> xarray([[-0.70709, -0.70709, 1.41418], [-1.397 , 0.508 , 0.889 ]])> x.var(axis=-1)array([0.99996, 1. ]) # floating point shenanigans> x.mean(axis=-1)array([-0., -0.])

二阶

标准规约乘法加法 + 偏差:

def linear(x, w, b): # [m, in], [in, out], [out] -> [m, out] return x @ w + b

二阶层通常被叫作图像(因为它们都从一个标量三维空间光点到另一个标量三维空间)。

GPT 框架

GPT 框架遵循 transformer 的框架:

但仅适用类比堆堆叠(数学公的单的左侧外):

GPT框架

概括来说,GPT 框架有三个外:

评节录+右方连在一起

一个 transformer 类比堆叠

一个光点到名该词的方法

在文档当中,它有点像这样:

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]

接下来我们将这三外的细节一齐细化。

连在一起

暂存筒连在一起

对于骨骼肌互联网来说,暂存筒 ID 本身并不是很好的列于示。首先,暂存筒 ID 的比较椭圆形出错地列于达了资讯(例如,如果在我们的名该词当中Apple = 5Table = 10 ,那么我们就意味着 2 * Table = Apple)。其次,对于骨骼肌互联网来说,单个数字的阶数并不高。

为探究决这些限制,我们将为了让该词乘积的优势,偏爱是通过进修连在一起乘法:

wte[inputs] # [n_embd] -> [n_seq, n_embd]

才会想一下,wte 是一个 [n_vocab, n_embd] 乘法。它作为一个查找列于,乘法当中的第 3 言道完全一致于我们名该词当中第 1 个暂存筒的进修乘积。wte[inputs] 适用幂数组数据库来检索完全一致于我们重定向当中每个暂存筒的乘积。

像我们互联网当中的其他反之亦然一样,wte 是进修的。意味著,它在受训开始时是随机加载的,然后通过分量攀升进言道更为新。

右方连在一起

Transformer 框架的一个怪癖是它没有难以实现右方。意味著,如果我们随机转回重定向,然后相应地终止转回转换成,转换成将与我们一开始从未转回重定向的状况相异(重定向的顺序对转换成没有任何冲击)。

当然,后缀的顺序是口语的一个关键外(duh),所以我们须要一些方法来将右方资讯序列到我们的重定向当中。为此,我们可以直接适用另一个进修的连在一起乘法:

wpe[range(len(inputs))] # [n_seq] -> [n_seq, n_embd]

回想一下,wpe 是一个 [n_ctx, n_embd] 乘法。乘法的第 3 言道构成一个标量,序列重定向当中第 1 个右方的资讯。与 wte 相似,这个乘法是在分量攀升上次当中进修的。

肯定,这将我们的静态限制在最大脱氧核糖核酸长度为 n_ctx。意味著,len(inputs)<= n_ctx 必须变为立。

除此以外

我们可以把我们的上标和右方连在一起加在独自,获取一个同时序列上标和右方资讯的除此以外连在一起。

# token + positional embeddingsx = wte[inputs] + wpe[range(len(inputs))] # [n_seq] -> [n_seq, n_embd]# x[i] represents the word embedding for the ith word + the positional# embedding for the ith position

类比堆叠

这是所有魔力时有发生的;也,也是浅层进修当中的 "浅层 "所在。我们将传输 n_layer 产物筒-类比块传输连在一起。

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

堆叠更为多的层使我们能够高度集中我们的互联网浅层。例如,GPT-3 有高达 96 层。另一方面,选项一个更为大的 n_embd 倍数可以让我们高度集中我们的互联网的跨距(例如,GPT-3 适用的连在一起尺寸为 12288)。

图像到Vocab

在我们的最终一步当中,我们将最终的 transformer 块的转换成光点到我们的形的单口语的机率分布上。

# projection to vocabx = 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]

肯定:

1、在进言道光点到 vocab 早先,我们首先将 x 通过最终一层标准规约化层。这是 GPT-2 框架所特有的。

2、我们即将再次度适用连在一起乘法 wte 进言道图像。其他 GPT 借助可以选项适用除此以外的进修二阶乘法进言道图像,但是资源共享连在一起乘法有几个用一处。

你可以耗费一些反之亦然(尽管在GPT-3的影响力也下,也或多或少不计)。

由于该乘法既都由到该词的射影,又都由从该词的射影,所以从理论上说,与持有两个除此以外的乘法远比,它不太可能才会懂得更为丰富的列于示。

3、我们亦非最终系统设计 softmax,所以我们的转换成将是范的单,而不是 0 和 1 间的机率。这样花钱有列于列出几个主因:

softmax 是单调,所以对于贪财调制来说,np.argmax(logits) 并不相同np.argmax(softmax(logits)),使得 softmax 变为为无用。

softmax 是不可逆,这意味着我们总是可以通过系统设计 softmax 从范的单到机率,但我们必须从机率回到范的单,所以为了给予最大的灵活性,我们转换成范的单数倍数有利于(例如,为了计算交错总能量死伤,与 log_softmax(logits)远比,取log(softmax(logits))在数倍数上是不有利于的。

光点到形的单口语的方法有时也被叫作口语可视化的两头。"两头 "是什么意思?一旦你的 GPT 被先于受训,你可以用其他类型的图像来去掉口语可视化两头,比如分类学两头,常用在某些分类学训练任务上对静态进言道变更。所以你的静态可以有多个两头,有点像九两头蛇。

这就是上佳的 GPT 框架,让我们意味著集中探究一下类比块在花钱什么。

类比块

transformer 类比块由两个长子层组变为:

多两头任何事物自我非议

定位的前馈骨骼肌互联网

def transformer_block(x, mlp, attn, ln_1, ln_2, n_head): # [n_seq, n_embd] -> [n_seq, n_embd] # multi-head causal self attention x = x + mha(layer_norm(x, **ln_1), **attn, n_head=n_head) # [n_seq, n_embd] -> [n_seq, n_embd] # position-wise feed forward network x = x + ffn(layer_norm(x, **ln_2), **mlp) # [n_seq, n_embd] -> [n_seq, n_embd] return x

每个长子层在其重定向上以及只剩联接都为了让了层标准规约化(即将长子层的重定向填入长子层的转换成上)。

肯定:

1、多两头任何事物自我非议是促成重定向间交流会的各种因素。在互联网的其他任何;也,该静态都不并不须要重定向 "看着 "对方。连在一起、右方前馈互联网、层规约和 vocab 的图像都是基于我们的重定向右方上操。对重定向间的关系进言道可视化的训练任务基本上由目光来完变为。

2、右方的单也前馈骨骼肌互联网只是一个普通的 2 层基本上联接骨骼肌互联网。这只是为我们的静态减小了一堆可进修的反之亦然,以促成进修。

3、在本来的变压筒科学论文当中,层范数被置于转换成层 _norm(x + sublayer(x)) 上,而我们将层规约置于重定向 x + sublayer(layer_norm(x)) 上以反之亦然 GPT-2。这被叫作先于规约,已被证明对提高变压筒的可靠性很不可忽视。

4、只剩联接(由ResNet拓展)有并不相同的用途:

更为容易简化浅层骨骼肌互联网(即有很多层的互联网)。这里的初衷是,我们为分量回流互联网发放 "--",使其更为容易简化互联网当中的以前层。

如果没有只剩联接,更为深的静态在减小顶层时可靠性才会攀升(不太可能是因为分量很难在不出错资讯的状况下全部流向浅层互联网)。只剩联接似乎给深层互联网带来了一些准确性的进一步提高。

可以为了让解决分量消失/冲击波的问题。

让我们对这两个长子层进言道更为集中地探究。

右方的单也前馈互联网

这只是一个具有 2 层的直观多层感知筒:

def ffn(x, c_fc, c_proj): # [n_seq, n_embd] -> [n_seq, n_embd] # project up a = gelu(linear(x, **c_fc)) # [n_seq, n_embd] -> [n_seq, 4*n_embd] # project back down x = linear(a, **c_proj) # [n_seq, 4*n_embd] -> [n_seq, n_embd] return x

我们只都从 n_embd 光点到一个更为高的阶数 4*n_embd,然后再次下跌到 n_embd。

鲜为人知一下,在我们的反之亦然原文当中,我们的 mlp 反之亦然是这样的:

"mlp": { "c_fc": {"b": [4*n_embd], "w": [n_embd, 4*n_embd]}, "c_proj": {"b": [n_embd], "w": [4*n_embd, n_embd]},}

多两头任何事物的自我非议

这一层不太可能是 transformer 当中总能思考的外。因此,让我们通过把每个该词裂解变为自己的外,来超出 "多两头任何事物的自我非议"。

目光

自身

任何事物

多两头

目光

我们从两头开始推导出原始变压筒科学论文当中提出的图层点积方程:

我们必需从博文当中改篇我们的目光借助:

def attention(q, k, v): # [n_q, d_k], [n_k, d_k], [n_k, d_v] -> [n_q, d_v] return softmax(q @ k.T / np.sqrt(q.shape[-1])) @ v

自我

当 q、k 和 v 都来自同一相关联时,我们即将进言道自我非议(即让我们的重定向脱氧核糖核酸非议自己):

def self_attention(x): # [n_seq, n_embd] -> [n_seq, n_embd] return attention(q=x, k=x, v=x)

我们可以通过引进 q、k、v 和目光转换成的光点来加强自我肯定。

def self_attention(x, w_k, w_q, w_v, w_proj): # [n_seq, n_embd] -> [n_seq, n_embd] # qkv projections q = x @ w_k # [n_seq, n_embd] @ [n_embd, n_embd] -> [n_seq, n_embd] k = x @ w_q # [n_seq, n_embd] @ [n_embd, n_embd] -> [n_seq, n_embd] v = x @ w_v # [n_seq, n_embd] @ [n_embd, n_embd] -> [n_seq, n_embd] # perform self attention x = attention(q, k, v) # [n_seq, n_embd] -> [n_seq, n_embd] # out projection x = x @ w_proj # [n_seq, n_embd] @ [n_embd, n_embd] -> [n_seq, n_embd] return x

这使我们的静态能够进修一个 q、k 和 v 的射影,最好地为了让目光分辨重定向间的关系。

如果我们把 w_q、w_k 和 w_v 新设变为一个单独的乘法 w_fc,进言道图像,然后划分结果,就可以把乘法加法的连续从 4 次减少到 2 次:

def self_attention(x, w_fc, w_proj): # [n_seq, n_embd] -> [n_seq, n_embd] # qkv projections x = x @ w_fc # [n_seq, n_embd] @ [n_embd, 3*n_embd] -> [n_seq, 3*n_embd] # split into qkv q, k, v = qkv = np.split(x, 3, axis=-1) # [n_seq, 3*n_embd] -> 3 of [n_seq, n_embd] # perform self attention x = attention(q, k, v) # [n_seq, n_embd] -> [n_seq, n_embd] # out projection x = x @ w_proj # [n_seq, n_embd] @ [n_embd, n_embd] = [n_seq, n_embd] return x

这样花钱的效率更为高一些,因为近代加速筒(GPU)可以更为好地为了让一个大的乘法加法,而不是 3 个独立自主的小的乘法加法以此类推时有发生。

最终,我们去除一般而言乘积以反之亦然 GPT-2 的借助,适用我们的二阶给定,并再次度命名我们的反之亦然以反之亦然我们的原文 linearparams。

def self_attention(x, c_attn, c_proj): # [n_seq, n_embd] -> [n_seq, n_embd] # qkv projections x = linear(x, **c_attn) # [n_seq, n_embd] -> [n_seq, 3*n_embd] # split into qkv q, k, v = qkv = np.split(x, 3, axis=-1) # [n_seq, 3*n_embd] -> 3 of [n_seq, n_embd] # perform self attention x = attention(q, k, v) # [n_seq, n_embd] -> [n_seq, n_embd] # out projection x = linear(x, **c_proj) # [n_seq, n_embd] @ [n_embd, n_embd] = [n_seq, n_embd] return x

鲜为人知一下,从我们的反之亦然原文当中,我们的 attn 反之亦然有点像这样:

"attn": { "c_attn": {"b": [3*n_embd], "w": [n_embd, 3*n_embd]}, "c_proj": {"b": [n_embd], "w": [n_embd, n_embd]},},

任何事物关系

我们目前的自我目光另设似乎问题,我们的重定向可以看着先于见!例如,如果我们的重定向是 ["not", "all", "heroes", "wear", "capes"],在自我非议长期,我们并不须要 "wear" 看着 "capes"。这意味着我们对 "wear" 的转换成机率才会有偏差,因为静态早已知道应该作答是 "capes"。这是不好的,由于我们的静态刚刚学才会,重定向的应该作答可以从重定向当中获取。

为了预防这种状况,我们须要以某种方的单也变更我们的目光乘法,以隐藏或掩盖我们的重定向,使其没有看着先于见。例如,让我们假装我们的目光乘法有点像这样:

not all heroes wear capes not 0.116 0.159 0.055 0.226 0.443 all 0.180 0.397 0.142 0.106 0.175heroes 0.156 0.453 0.028 0.129 0.234 wear 0.499 0.055 0.133 0.017 0.295 capes 0.089 0.290 0.240 0.228 0.153

每一言道完全一致于一个查询,每一列完全一致于一个键。在这种状况下,看一下 "wear"这一言道,你可以看着它在最终一列参加 "capes",二阶为0.295。为了预防这种状况,我们要将该以下内容另设为0.0。

not all heroes wear capes not 0.116 0.159 0.055 0.226 0.443 all 0.180 0.397 0.142 0.106 0.175heroes 0.156 0.453 0.028 0.129 0.234 wear 0.499 0.055 0.133 0.017 0. capes 0.089 0.290 0.240 0.228 0.153

有时候,为了预防我们的重定向当中的所有查询看向先于见,我们把所有的右方都另设为0。

not all heroes wear capes not 0.116 0. 0. 0. 0. all 0.180 0.397 0. 0. 0.heroes 0.156 0.453 0.028 0. 0. wear 0.499 0.055 0.133 0.017 0. capes 0.089 0.290 0.240 0.228 0.153

我们将这叫作看得见。上述看得见方法的一个问题是,我们的言道数之和不再次是 1(因为我们在系统设计 softmax 后将其另设为 0)。为了能够我们的言道之和为 1,我们须要在系统设计 softmax 早先变更我们的肯定乘法。

这可以通过在 softmax 早先另设要被屏蔽的以下内容来借助:

def attention(q, k, v, mask): # [n_q, d_k], [n_k, d_k], [n_k, d_v], [n_q, n_k] -> [n_q, d_v] return softmax(q @ k.T / np.sqrt(q.shape[-1]) + mask) @ v

其当中乘法(常用):maskn_seq=5

我们适用 -1e10 而不是 -np.inf,因为 -np.inf 才会引致 nans。

在我们的目光乘法当中转至掩码,而不是确切地将数倍数另设为 -1e10,因为意味著上,任何数字连带 -inf 就是 -inf。

我们可以在 NumPy 当中用(1-np.tri(n_seq))计算掩码乘法 * -1e10.

综上所述,我们获取:

def attention(q, k, v, mask): # [n_q, d_k], [n_k, d_k], [n_k, d_v], [n_q, n_k] -> [n_q, d_v] return softmax(q @ k.T / np.sqrt(q.shape[-1]) + mask) @ vdef causal_self_attention(x, c_attn, c_proj): # [n_seq, n_embd] -> [n_seq, n_embd] # qkv projections x = linear(x, **c_attn) # [n_seq, n_embd] -> [n_seq, 3*n_embd] # split into qkv q, k, v = qkv = np.split(x, 3, axis=-1) # [n_seq, 3*n_embd] -> 3 of [n_seq, n_embd] # causal mask to hide future inputs from being attended to causal_mask = (1 - np.tri(x.shape[0])) * -1e10 # [n_seq, n_seq] # perform causal self attention x = attention(q, k, v, causal_mask) # [n_seq, n_embd] -> [n_seq, n_embd] # out projection x = linear(x, **c_proj) # [n_seq, n_embd] @ [n_embd, n_embd] = [n_seq, n_embd] return x

多两头

我们可以通过执言道 n_head 除此以外的目光计算来必要性修改我们的借助,将我们的查询、键和倍数划分变为两头:

def mha(x, c_attn, c_proj, n_head): # [n_seq, n_embd] -> [n_seq, n_embd] # qkv projection x = linear(x, **c_attn) # [n_seq, n_embd] -> [n_seq, 3*n_embd] # split into qkv qkv = np.split(x, 3, axis=-1) # [n_seq, 3*n_embd] -> [3, n_seq, n_embd] # split into heads qkv_heads = list(map(lambda x: np.split(x, n_head, axis=-1), qkv)) # [3, n_seq, n_embd] -> [3, n_head, n_seq, n_embd/n_head] # causal mask to hide future inputs from being attended to causal_mask = (1 - np.tri(x.shape[0])) * -1e10 # [n_seq, n_seq] # perform attention over each head out_heads = [attention(q, k, v, causal_mask) for q, k, v in zip(*qkv_heads)] # [3, n_head, n_seq, n_embd/n_head] -> [n_head, n_seq, n_embd/n_head] # merge heads x = np.hstack(out_heads) # [n_head, n_seq, n_embd/n_head] -> [n_seq, n_embd] # out projection x = linear(x, **c_proj) # [n_seq, n_embd] -> [n_seq, n_embd] return x

这里减小了三个方法:

1.将 q、k、v 分变为 n_head 两头:

# split into headsqkv_heads = list(map(lambda x: np.split(x, n_head, axis=-1), qkv)) # [3, n_seq, n_embd] -> [n_head, 3, n_seq, n_embd/n_head]

2.计算每个两头部的目光:

# perform attention over each headout_heads = [attention(q, k, v) for q, k, v in zip(*qkv_heads)] # [n_head, 3, n_seq, n_embd/n_head] -> [n_head, n_seq, n_embd/n_head]

3.新设每个两头的转换成:

# merge headsx = np.hstack(out_heads) # [n_head, n_seq, n_embd/n_head] -> [n_seq, n_embd]

肯定,这将每个目光计算的阶数从 n_embd 减少到 n_embd/n_head。为了降低阶数,我们的静态在通过目光建立关系静态时获取了额外的长子三维空间。例如,无论如何一个目光两头都由将代该词与代该词同义的人联系起来。无论如何另一个不太可能都由按时期对短语进言道除此以外。另一个不太可能只是都由鉴别哪些该词是并不一定,哪些不是。虽然,它不太可能只是另一个骨骼肌互联网鸽长子。

我们编撰写的文档在一个尿素当中按以此类推对每个两头进言道目光计算(一次一个),这样的效率并不高。在实践当中,你才会希望并言道地进言道这些计算。为了直观起见,我们还是让它按以此类推进言道。

曾一度,我们终于完变为了我们的 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.

结果证明,它是直接的!

我们可以适用列于列出 Dockerfile 测试者我们的借助是否与 OpenAI 的正的单 GPT-2 驱动器库给出相异的结果:

docker build -t "openai-gpt-2" ""docker run -dt "openai-gpt-2" ---name "openai-gpt-2-app"docker exec -it "openai-gpt-2-app" /bin/bash -c 'python3 src/interactive_conditional_samples.py ---length 8 ---model_type 124M ---top_k 1'# paste "Alan Turing theorized that computers would one day become" when prompted

这应该给出相异的结果:

the most powerful machines on the planet.

以上就是 Jay Mody 在网站的细节,大家有兴趣的可以自己试一下~或多或少影响力也,GPT 的受训是并不标准规约的,比较于口语可视化死伤的分量攀升。当然还有很多长一处,但这些都是标准规约的东西。受训一个好的 GPT 静态的真正秘诀是能够扩展原始数据和静态。对此,你有哪些立场呢~

如何调理儿童脾胃虚弱
哪种复合维生素比较好
肠胃不适恶心想吐怎么办
便秘排便少吃什么好
小孩不爱吃饭怎么办吃什么调理
相关阅读
股票市场提问:请问公司在万向财务公司一月份存款数额多少?利息收入是多少?请及...

投资者追问:劝问该公司在飞轮财务该公司一月份存款额度多少?利息收入是多少?劝及时会面时!董秘回答承德露露SZ000848:您好,该公司在飞轮财务有限该公司存款额度及利息收入...

给领导起程消息,为什么不要说“好的”,这是我见过最好的答案

兼职中当你接到或者主导致信的死讯,是不是只就会为了让“好的”就默默潜水? 才刚步入求职的小编也就会比如说地为了让“好的”,直到有结缘好心警告,给主导为了让切忌绝不会再说道“好的”,...

中国香港飞机引擎在南中国海上空失效322人生死存亡是因为燃油进水?

航空器涡轮引擎并一定会有被调小,还保持一致百分之74的的动力输出。他吓坏了。但是,此时他们早已一定会必要复飞,并不需要寻宝飞越。航空器总重200吨,他们能必要受困吗? 航空...

投资者提问:你好,请问贵公司在AI这块有无涉猎,有无相应的武技术储备,AI武技...

外资者提问:爱,不对贵母公司在AI这块不一定通晓,不一定相应的技术储备,AI技术应用这块不一定相应的规模化建设呢?董秘问到厦门信达SZ000701:您好,母公司信息科技领域...

也毕竟要么就是没时间,要么就是不愿意

我现在还时会在腾讯和QQ上醒天的,醒天的实例也多是网路上和非常少几个基本上的密友,之外是几个网路上,属于不冷不热,半生不熟的,其实醒天也就是几句客套的话。但是,不管是熟悉的还是陌生的醒友,我...