中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

學(xué)院 網(wǎng)站 兩學(xué)一做深圳網(wǎng)站seo外包公司哪家好

學(xué)院 網(wǎng)站 兩學(xué)一做,深圳網(wǎng)站seo外包公司哪家好,簡(jiǎn)述jsp網(wǎng)站架構(gòu),金融理財(cái)網(wǎng)站建設(shè)方案歡迎來到雲(yún)閃世界。Andrej Karpathy 是人工智能 (AI) 領(lǐng)域的頂尖研究人員之一。他是 OpenAI 的創(chuàng)始成員之一,曾領(lǐng)導(dǎo)特斯拉的 AI 部門,目前仍處于 AI 社區(qū)的前沿。 在第一部分中,我們重點(diǎn)介紹如何實(shí)現(xiàn) GPT-2 的架構(gòu)。雖然 GPT-2 于 2018 年由 …

歡迎來到雲(yún)閃世界。Andrej Karpathy 是人工智能 (AI) 領(lǐng)域的頂尖研究人員之一。他是 OpenAI 的創(chuàng)始成員之一,曾領(lǐng)導(dǎo)特斯拉的 AI 部門,目前仍處于 AI 社區(qū)的前沿。

? ? ? ? 在第一部分中,我們重點(diǎn)介紹如何實(shí)現(xiàn) GPT-2 的架構(gòu)。雖然 GPT-2 于 2018 年由 OpenAI 開源,但它是用 Tensor Flow 編寫的,這是一個(gè)比 PyTorch 更難調(diào)試的框架。因此,我們將使用更常用的工具重新創(chuàng)建 GPT-2。僅使用我們今天要?jiǎng)?chuàng)建的代碼,您就可以創(chuàng)建自己的 LLM!

塊大小— 告訴我們 Transformer 可以處理輸入長(zhǎng)度中的多少個(gè)位置。一旦超過此限制,性能就會(huì)下降,因?yàn)槟仨毣乩@(您可以在我的 Long RoPE 博客中詳細(xì)了解我們?nèi)绾螖U(kuò)展此限制,而無需從頭開始訓(xùn)練新模型)

詞匯量——告訴我們模型能夠理解和使用多少個(gè)獨(dú)特的標(biāo)記。一般來說,研究人員發(fā)現(xiàn),詞匯量越大,模型對(duì)語言的理解就越精確,并且能夠捕捉到響應(yīng)中的更多細(xì)微差別。

層 —我們神經(jīng)網(wǎng)絡(luò)的隱藏層的一部分。具體來說,我們指的是下面灰色框中顯示的重復(fù)計(jì)算的次數(shù):

我們的模型中的一層

嵌入——我們傳遞給模型的數(shù)據(jù)的向量表示。

多頭注意力——我們不是運(yùn)行一次注意力,而是運(yùn)行 n 次,然后將所有結(jié)果連接在一起以獲得最終結(jié)果。

讓我們進(jìn)入代碼吧!

GPT 類及其參數(shù)

@dataclass
class GPTConfig:block_size : int = 1024vocab_size : int = 50257n_layer : int = 12n_head : int = 12n_embd : int = 768

首先,我們?cè)?GPTConfig 類中設(shè)置 5 個(gè)超參數(shù)。block_size似乎與 和 一樣有些隨意n_layer。n_head換句話說,這些值是根據(jù)研究人員認(rèn)為具有最佳性能的值經(jīng)驗(yàn)選擇的。 此外,我們選擇 786,因?yàn)?code>n_embd這是為 GPT-2 論文選擇的值,我們決定模仿它。

但是,是根據(jù)我們將使用的 GPT-2 標(biāo)記器vocab_size設(shè)置的。GPT-2 標(biāo)記器是使用字節(jié)對(duì)編碼算法創(chuàng)建的(在此處信息)。它從一組初始詞匯開始(在我們的例子中是 256 個(gè)),然后根據(jù)新詞匯出現(xiàn)在訓(xùn)練集中的頻率,通過訓(xùn)練數(shù)據(jù)創(chuàng)建新詞匯。它會(huì)一直這樣做,直到達(dá)到極限(在我們的例子中是 50,000)。最后,我們將詞匯留作內(nèi)部使用(在我們的例子中是結(jié)束標(biāo)記字符)。把它們加起來,我們得到了 50,257。tiktoken

class GPT(nn.Module):def __init__(self, config):super().__init__()self.config = config# ...

設(shè)置好配置后,我們創(chuàng)建一個(gè) GPT 類,它是 torchnn.Module類的一個(gè)實(shí)例。這是所有 PyTorch 神經(jīng)網(wǎng)絡(luò)的基類,因此通過使用它,我們可以訪問 PyTorch 針對(duì)這些類型的模型的所有優(yōu)化。每個(gè)優(yōu)化nn.Module都有一個(gè)forward函數(shù)來定義模型前向傳遞期間發(fā)生的情況(稍后會(huì)詳細(xì)介紹)。

我們首先運(yùn)行基類中的超級(jí)構(gòu)造函數(shù),然后創(chuàng)建一個(gè)transformer對(duì)象ModuleDict。創(chuàng)建它是因?yàn)樗梢宰屛覀兿駥?duì)象一樣進(jìn)行索引transformer,當(dāng)我們想要從 HuggingFace 加載權(quán)重時(shí),以及當(dāng)我們想要調(diào)試并快速瀏覽我們的模型時(shí),它都會(huì)派上用場(chǎng)。

class GPT(nn.Module):def __init__(self, config):# ...self.transformer = nn.ModuleDict(dict(wte = nn.Embedding(config.vocab_size, config.n_embd),wpe = nn.Embedding(config.block_size, config.n_embd),h = nn.ModuleList([Block(config) for _ in range(config.n_layer)]),ln_f = nn.LayerNorm(config.n_embd)))

這里transformer有 4 個(gè)主要部分需要加載:token 嵌入的權(quán)重 (?wte)、位置編碼的權(quán)重 (?wpe)、隱藏層 (?h) 和層規(guī)范化 ( )。此設(shè)置主要ln_f遵循“Attention is All You Need”中 Transformer 架構(gòu)的解碼器部分(輸出嵌入 ~?、位置編碼 ~?、隱藏層 ~ )。一個(gè)關(guān)鍵的區(qū)別是,在我們的架構(gòu)中,所有隱藏層完成后,我們還有一個(gè)額外的規(guī)范化層。wtewtehln_f

“Attention is All You Need”中展示的解碼器架構(gòu)的一半

wte和都是wpe嵌入,因此我們自然nn.Embedding會(huì)使用 類 來表示它們。我們的隱藏層是 Transformer 的大部分邏輯所在,所以我稍后會(huì)詳細(xì)介紹。現(xiàn)在,只需注意我們正在創(chuàng)建對(duì)象的循環(huán),以便Block我們擁有n.layer。最后,我們使用內(nèi)置的nn.LayerNormln_f它將根據(jù)以下等式對(duì)我們的輸出進(jìn)行規(guī)范化(其中 x 和 y 是輸入和輸出,E[x] 是平均值,γ 和 β 是可學(xué)習(xí)的權(quán)重)。

PyTorch 中的層歸一化方程

class GPT(nn.Module):def __init__(self, config):# ...self.lm_head = nn.Linear(config.n_embd, config.vocab_size, bias=False)# weight sharing schemeself.transformer.wte.weight = self.lm_head.weight# initalize weightsself.apply(self._init_weights)

接下來,我們?cè)O(shè)置網(wǎng)絡(luò)的最后一個(gè)線性層,它將生成模型的邏輯。在這里,我們從模型的嵌入維度(768)投影到模型的詞匯量(50,257)。這里的想法是,我們采用了隱藏狀態(tài)并將其擴(kuò)展以映射到我們的詞匯表上,以便我們的解碼器頭可以使用每個(gè)詞匯表上的值來確定下一個(gè)標(biāo)記應(yīng)該是什么。

最后,在我們的構(gòu)造函數(shù)中,我們有一個(gè)有趣的優(yōu)化,我們告訴模型使標(biāo)記器權(quán)重與線性層權(quán)重相同。這樣做是因?yàn)槲覀兿M€性層和標(biāo)記器對(duì)標(biāo)記有相同的理解(如果兩個(gè)標(biāo)記在輸入模型時(shí)相似,則模型輸出時(shí)相同的兩個(gè)標(biāo)記也應(yīng)該相似)。最后,我們初始化模型的權(quán)重,以便我們可以開始訓(xùn)練。

class GPT(nn.Module):
# ...def forward(self, idx, targets=None):B, T = idx.size() assert T <= self.config.block_size, f"maximum sequence length breached"pos = torch.arange(0, T, dtype=torch.long, device=idx.device)pos_emb = self.transformer.wpe(pos)tok_emb = self.transformer.wte(idx)x = tok_emb + pos_emb # hidden broadcastfor block in self.transformer.h:x = block(x)x = self.transformer.ln_f(x)logits = self.lm_head(x)loss = Noneif targets is not None:loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1))return logits, loss

我們的前向函數(shù)精確地說明了我們的模型在前向傳遞過程中的行為方式。我們首先驗(yàn)證我們的序列長(zhǎng)度不大于我們配置的最大值 (?block_size)。一旦驗(yàn)證通過,我們將創(chuàng)建一個(gè)值為 0 到 T-1 的張量(例如,如果 T = 4,我們將有 tensor([0, 1, 2, 3]) 并通過我們的位置嵌入權(quán)重運(yùn)行它們。完成后,我們將通過 token 嵌入權(quán)重運(yùn)行輸入張量。

我們將 token 和位置嵌入組合成x,需要廣播來組合它們。由于 大于tok_embpos_emb在我們的示例中為 50257 vs 1024),x 的尺寸為tok_emb?,F(xiàn)在是我們的隱藏狀態(tài),我們將通過 for 循環(huán)將其傳遞到隱藏層。我們小心地在每次通過一個(gè)塊后x進(jìn)行更新。x

接下來,我們x通過 LayerNormalization進(jìn)行歸一化ln_f,然后進(jìn)行線性投影以獲得預(yù)測(cè)下一個(gè)標(biāo)記所需的 logit。如果我們正在訓(xùn)練模型(我們通過參數(shù)發(fā)出信號(hào)targets),我們將計(jì)算我們剛剛生成的 logit 和變量中保存的地面真實(shí)值之間的交叉熵targets。我們通過損失函數(shù)實(shí)現(xiàn)這一點(diǎn)cross_entropy。為了正確做到這一點(diǎn),我們需要通過 將logits和轉(zhuǎn)換target為正確的形狀.view()。我們要求 pytorch 在通過 -1 時(shí)推斷出正確的大小。

這個(gè)類中還有一個(gè)函數(shù),即初始化函數(shù),但我們稍后會(huì)討論初始化邏輯?,F(xiàn)在,讓我們深入研究 Block 邏輯,它將幫助我們實(shí)現(xiàn)多頭注意力和 MLP。

塊類

class Block(nn.Module):def __init__(self, config):super().__init__()self.ln_1 = nn.LayerNorm(config.n_embd)self.attn = CausalSelfAttention(config)self.ln_2 = nn.LayerNorm(config.n_embd)self.mlp = MLP(config)
# ...

Block 被實(shí)例化為nn.Module,因此我們也在開始時(shí)調(diào)用超級(jí)構(gòu)造函數(shù)進(jìn)行優(yōu)化。接下來,我們?cè)O(shè)置與“Attention is All You Need”論文中所述的相同計(jì)算 — 2 層規(guī)范化、注意力計(jì)算和通過 MLP 的前饋層。

《注意力就是一切》中的隱藏層

class Block(nn.Module):
# ...def forward(self, x):x = x + self.attn(self.ln_1(x))x = x + self.mlp(self.ln_2(x))return x

然后,我們定義一個(gè)forward函數(shù),PyTorch 將在模型的每次前向傳遞中調(diào)用該函數(shù)。請(qǐng)注意,這里我們做的事情與“注意力就是你所需要的一切”不同。我們分別將層規(guī)范化設(shè)置為在注意力和前饋之前發(fā)生。這是 GPT-2 論文見解的一部分,你可以看到,像這樣進(jìn)行微小的改變會(huì)帶來巨大的不同。請(qǐng)注意,原始張量的添加保持在相應(yīng)的相同位置。當(dāng)我們?cè)O(shè)置權(quán)重初始化函數(shù)時(shí),這 2 個(gè)添加將非常重要。

這個(gè)類是一個(gè)很好的抽象,因?yàn)樗屛覀兛梢越粨Q注意力的實(shí)現(xiàn)或者選擇除了 MLP 之外的另一種前饋函數(shù),而不必大規(guī)模重構(gòu)代碼。

CausalSelfAttention 類

class CausalSelfAttention(nn.Module):def __init__(self, config):super().__init__()assert config.n_embd % config.n_head == 0self.c_attn = nn.Linear(config.n_embd, 3*config.n_embd)self.c_proj = nn.Linear(config.n_embd, config.n_embd)self.c_proj.NANOGPT_SCALE_INIT = 1self.n_head = config.n_headself.n_embd = config.n_embdself.register_buffer('bias', torch.tril(torch.ones(config.block_size, config.block_size)).view(1,1, config.block_size, config.block_size))
# ...

注意力是我們模型的重要組成部分,因此這里自然有許多配置。我們使用 assert 語句作為調(diào)試工具,以確保我們傳遞的配置維度是兼容的。然后我們創(chuàng)建一些輔助函數(shù),這些函數(shù)將在我們進(jìn)行自我注意力時(shí)為我們提供幫助。首先,我們有c_attn和,c_proj它們是線性投影,可將我們的隱藏狀態(tài)轉(zhuǎn)換為注意力計(jì)算所需的新維度。c_proj.NANOGPT_SCALE_INIT是我們?cè)诖颂幒?MLP 中設(shè)置的標(biāo)志,它將幫助我們稍后進(jìn)行權(quán)重初始化(實(shí)際上,這可以命名為任何名稱)。

最后,我們告訴 torch 創(chuàng)建一個(gè)名為 的緩沖區(qū),該緩沖區(qū)在訓(xùn)練期間不會(huì)更新bias。偏差將是一個(gè)尺寸為block_sizex的下三角矩陣block_size,然后我們將其轉(zhuǎn)換為尺寸為 1 x 1 x?block_sizex 的4D 張量block_size。1 x 1 已完成,以便我們可以在單個(gè)通道中批量計(jì)算這些。此緩沖區(qū)將用于對(duì)我們的多頭注意力應(yīng)用掩碼。

class CausalSelfAttention(nn.Module):
# ...def forward(self, x):B, T, C = x.size() # batch size, sequence length, channelsqkv = self.c_attn(x)q, k, v = qkv.split(self.n_embd, dim=2)# transpose is done for efficiency optimizationk = k.view(B, T, self.n_head, C // self.n_head).transpose(1, 2)q = q.view(B, T, self.n_head, C // self.n_head).transpose(1, 2)v = v.view(B, T, self.n_head, C // self.n_head).transpose(1, 2)att = (q @ k.transpose(-2,-1)) * (1.0 / math.sqrt(k.size(-1)))att = att.masked_fill(self.bias[:, :, :T, :T] == 0, float("-inf"))att = F.softmax(att, dim=-1)y = att @ vy = y.transpose(1,2).contiguous().view(B, T, C)y = self.c_proj(y)return y

現(xiàn)在開始實(shí)現(xiàn)注意力機(jī)制,重點(diǎn)是讓它在 torch 中表現(xiàn)優(yōu)異。逐行來看,我們首先找到輸入張量 x 中的批處理大小、序列長(zhǎng)度和通道。然后,我們將調(diào)用c_attn之前的函數(shù)將隱藏狀態(tài)投影到我們需要的維度中。然后,我們將結(jié)果拆分為 3 個(gè) (B、T、C) 形狀的張量(具體來說,一個(gè)用于查詢,一個(gè)用于鍵,一個(gè)用于值)。

然后,我們調(diào)整 q、k 和 v 的維度,以便能夠高效地對(duì)這些維度進(jìn)行多頭注意。通過將維度從 (B, T, C) 更改為 (B, T, self.n_head, C // self.n_head),我們正在劃分?jǐn)?shù)據(jù),以便每個(gè)頭部都有自己獨(dú)特的數(shù)據(jù)來操作。我們轉(zhuǎn)置視圖,以便我們可以制作T第三維度和self.n_head第二維度,從而讓我們更容易地連接頭部。

注意力方程式來自“注意力就是你所需要的一切”

現(xiàn)在我們有了值,我們可以開始計(jì)算了。我們?cè)诓樵兒玩I之間執(zhí)行矩陣乘法(確保將鍵轉(zhuǎn)置為正確的方向),然后除以 k 大小的平方根。經(jīng)過此計(jì)算后,我們應(yīng)用來自寄存器的偏差,以便未來 token 的注意力數(shù)據(jù)不會(huì)影響當(dāng)前的 token(這就是為什么我們只對(duì)時(shí)間和通道維度大于 T 的 token 應(yīng)用掩碼的原因)。完成后,我們應(yīng)用 softmax 僅傳遞某些信息。

一旦打開掩碼,我們將值乘以 v,然后將值轉(zhuǎn)置回 (B, T, self.n_head, C // self.n_head) 設(shè)置。我們調(diào)用.contiguous()以確保內(nèi)存中的所有數(shù)據(jù)都彼此相鄰排列,最后將張量轉(zhuǎn)換回它所帶的 (B, T, C) 維度(因此,在此步驟中連接我們的注意力頭)。

最后,我們使用線性投影c_proj轉(zhuǎn)換回隱藏狀態(tài)的原始維度。

MLP 類

class MLP(nn.Module):def __init__(self, config):super().__init__()self.c_fc = nn.Linear(config.n_embd, 4 * config.n_embd)self.gelu = nn.GELU(approximate="tanh")self.c_proj = nn.Linear(4 * config.n_embd, config.n_embd)self.c_proj.NANOGPT_SCALE_INIT = 1
# ...

與之前的所有類一樣,MLP 繼承自nn.Module。我們首先設(shè)置一些輔助函數(shù)——特別是c_fcc_proj線性投影層,分別從我們的嵌入擴(kuò)展到 4 倍大小,然后再擴(kuò)大回來。接下來,我們有 GELU。Karpathy 強(qiáng)調(diào)說,這里的近似參數(shù)只是為了讓我們能夠緊密匹配 GPT-2 論文而設(shè)置的。雖然當(dāng)時(shí) GELU 的近似是必要的,但現(xiàn)在我們不再需要近似——我們可以精確計(jì)算。

class MLP(nn.Module):
# ...def forward(self, x):x = self.c_fc(x)x = self.gelu(x)x = self.c_proj(x)return x

那么我們的前向傳遞就相對(duì)簡(jiǎn)單了。我們?cè)谳斎霃埩可险{(diào)用每個(gè)函數(shù)并返回最終結(jié)果。

擁抱臉部連接代碼

由于 GPT-2 是開源的,因此可以在 Hugging Face 上使用。雖然我們的目標(biāo)是訓(xùn)練我們自己的模型,但能夠?qū)⑽覀兊慕Y(jié)果與 OpenAI 在訓(xùn)練中發(fā)現(xiàn)的結(jié)果進(jìn)行比較還是不錯(cuò)的。為了做到這一點(diǎn),我們有下面的函數(shù)來提取權(quán)重并將其填充到我們的GPT類中。

此代碼還允許我們重復(fù)使用此代碼來從 Hugging Face 中提取基礎(chǔ)模型并對(duì)其進(jìn)行微調(diào)(進(jìn)行了一些修改,因?yàn)槟壳八鼉H針對(duì) gpt-2 進(jìn)行了優(yōu)化)。

class GPT(nn.Module):
# ...@classmethoddef from_pretrained(cls, model_type):"""Loads pretrained GPT-2 model weights from huggingface"""assert model_type in {'gpt2', 'gpt2-medium', 'gpt2-large', 'gpt2-xl'}from transformers import GPT2LMHeadModelprint("loading weights from pretrained gpt: %s" % model_type)# n_layer, n_head and n_embd are determined from model_typeconfig_args = {'gpt2':         dict(n_layer=12, n_head=12, n_embd=768),  # 124M params'gpt2-medium':  dict(n_layer=24, n_head=16, n_embd=1024), # 350M params'gpt2-large':   dict(n_layer=36, n_head=20, n_embd=1280), # 774M params'gpt2-xl':      dict(n_layer=48, n_head=25, n_embd=1600), # 1558M params}[model_type]config_args['vocab_size'] = 50257 # always 50257 for GPT model checkpointsconfig_args['block_size'] = 1024 # always 1024 for GPT model checkpoints# create a from-scratch initialized minGPT modelconfig = GPTConfig(**config_args)model = GPT(config)sd = model.state_dict()sd_keys = sd.keys()sd_keys = [k for k in sd_keys if not k.endswith('.attn.bias')] # discard this mask / buffer, not a param
# ...

從頂部開始,我們引入 HuggingFace 的transformers庫并設(shè)置在 GPT-2 模型的不同變體之間變化的超參數(shù)。由于和vocab_size沒有block_size變化,您可以看到我們將它們硬編碼進(jìn)去。然后我們將這些變量傳遞到GPTConfig之前的類中,然后實(shí)例化模型對(duì)象(GPT)。最后,我們從模型中刪除所有以結(jié)尾的鍵.attn.bias,因?yàn)檫@些不是權(quán)重,而是我們之前設(shè)置的寄存器,用于幫助我們的注意力函數(shù)。

class GPT(nn.Module):
# ...@classmethoddef from_pretrained(cls, model_type):
# ...model_hf = GPT2LMHeadModel.from_pretrained(model_type)sd_hf = model_hf.state_dict()# copy while ensuring all of the parameters are aligned and match in names and shapessd_keys_hf = sd_hf.keys()sd_keys_hf = [k for k in sd_keys_hf if not k.endswith('.attn.masked_bias')] # ignore these, just a buffersd_keys_hf = [k for k in sd_keys_hf if not k.endswith('.attn.bias')] # same, just the mask (buffer)transposed = ['attn.c_attn.weight', 'attn.c_proj.weight', 'mlp.c_fc.weight', 'mlp.c_proj.weight']# basically the openai checkpoints use a "Conv1D" module, but we only want to use a vanilla Linear# this means that we have to transpose these weights when we import themassert len(sd_keys_hf) == len(sd_keys), f"mismatched keys: {len(sd_keys_hf)} != {len(sd_keys)}"

接下來,我們從 HuggingFace 類中加載模型GPT2LMHeadModel。我們從這個(gè)模型中取出鍵,同樣忽略attn.masked_biasattn.bias鍵。然后我們有一個(gè)斷言來確保 hugging face 模型中的鍵數(shù)與我們模型中的鍵數(shù)相同。

class GPT(nn.Module):
# ...@classmethoddef from_pretrained(cls, model_type):
# ...for k in sd_keys_hf:if any(k.endswith(w) for w in transposed):# special treatment for the Conv1D weights we need to transposeassert sd_hf[k].shape[::-1] == sd[k].shapewith torch.no_grad():sd[k].copy_(sd_hf[k].t())else:# vanilla copy over the other parametersassert sd_hf[k].shape == sd[k].shapewith torch.no_grad():sd[k].copy_(sd_hf[k])return model

為了完善該函數(shù),我們循環(huán)遍歷 Hugging Face 模型中的每個(gè)鍵,并將其權(quán)重添加到我們模型中的相應(yīng)鍵。有些鍵需要進(jìn)行操作,以便它們適合我們使用的數(shù)據(jù)結(jié)構(gòu)。我們運(yùn)行該函數(shù).t()將 hugging face 矩陣轉(zhuǎn)置為我們所需的尺寸。對(duì)于其余部分,我們直接復(fù)制它們。你會(huì)注意到我們正在使用torch.no_grad()。這告訴 torch 它不需要緩存模型反向傳播的值,這是另一個(gè)使其運(yùn)行更快的優(yōu)化。

生成我們的第一個(gè)預(yù)測(cè)(采樣循環(huán))

有了現(xiàn)在的類,我們可以運(yùn)行模型并讓它給我們輸出標(biāo)記(只需確保如果你按順序執(zhí)行此操作,請(qǐng)?jiān)?GPT 構(gòu)造函數(shù)中注釋掉 _init_weights 調(diào)用)。下面的代碼顯示了我們?nèi)绾巫龅竭@一點(diǎn)。

device = "cpu"
if torch.cuda.is_available():device = "cuda"
elif hasattr(torch.backends, "mps") and torch.backends.mps.is_available():device = "mps"
print(f"device {device}")torch.manual_seed(1337)model = GPT(GPTConfig())
model.eval()
model.to(device)

我們首先確定可以使用哪些設(shè)備。Cuda 是 NVIDIA 的平臺(tái),可以運(yùn)行極快的 GPU 計(jì)算,因此如果我們可以使用使用 CUDA 的芯片,我們就會(huì)使用它們。如果我們無法訪問,但我們使用的是 Apple Silicon,那么我們將使用它。最后,如果我們兩者都沒有,那么我們會(huì)回到 CPU(這將是最慢的,但每臺(tái)計(jì)算機(jī)都有一個(gè),所以我們知道我們?nèi)匀豢梢栽谒厦孢M(jìn)行訓(xùn)練)。

然后,我們使用默認(rèn)配置實(shí)例化我們的模型,并將模型置于“?eval”模式 —(這會(huì)執(zhí)行許多操作,例如禁用 dropout,但從高層次來看,它可以確保我們的模型在推理過程中更加一致)。設(shè)置完成后,我們將模型移動(dòng)到我們的設(shè)備上。請(qǐng)注意,如果我們想使用 HuggingFace 權(quán)重而不是我們的訓(xùn)練權(quán)重,我們將修改倒數(shù)第三行以讀取:model = GPT.from_pretrained(‘gpt2’)

import tiktoken
enc = tiktoken.get_encoding('gpt2')
tokens = enc.encode("Hello, I'm a language model,")
tokens = torch.tensor(tokens, dtype=torch.long)
tokens = tokens.unsqueeze(0).repeat(num_return_sequences, 1)
x = tokens.to(device)

我們現(xiàn)在引入tiktokengpt2 編碼并讓其標(biāo)記化我們的提示。我們獲取這些標(biāo)記并將其放入張量中,然后在下面的行中將其轉(zhuǎn)換為批次。unsqueeze()將向張量添加一個(gè)大小為 1 的新的第一個(gè)維度,并將在第一個(gè)維度內(nèi)repeat重復(fù)整個(gè)張量num_return_sequences時(shí)間,在第二個(gè)維度內(nèi)重復(fù)一次。我們?cè)谶@里所做的是格式化我們的數(shù)據(jù)以適合我們的模型期望的批處理模式。具體來說,我們現(xiàn)在匹配 (B, T) 格式:num_return_sequencesx 編碼的提示長(zhǎng)度。一旦我們將輸入張量傳遞到模型的開頭,我們的wtewpe將創(chuàng)建 C 維度。

while x.size(1) < max_length:with torch.no_grad():logits, _ = model(x)logits = logits[:, -1, :]probs = F.softmax(logits, dim=-1)topk_probs, topk_indices = torch.topk(probs, 50, dim=-1)ix = torch.multinomial(topk_probs, 1)xcol = torch.gather(topk_indices, -1, ix)x = torch.cat((x, xcol), dim=1)

現(xiàn)在它們已經(jīng)準(zhǔn)備好了,我們將它們發(fā)送到設(shè)備并開始采樣循環(huán)。該循環(huán)將完全是前向傳遞,因此我們將其包裝在中torch.no_grad以阻止其緩存任何后向傳播。我們的 logits 形狀為 (batch_size, seq_len, vocab_size) — (B,T,C),其中 C 位于模型的前向傳遞之后。

我們只需要序列中的最后一項(xiàng)來預(yù)測(cè)下一個(gè)標(biāo)記,因此我們?nèi)〕?code>[:, -1, :]然后我們獲取這些 logit 并將其運(yùn)行softmax以獲得標(biāo)記概率。取前 50 名,然后我們選擇前 50 名的隨機(jī)索引并將其選為我們的預(yù)測(cè)標(biāo)記。然后我們獲取有關(guān)該信息并將其添加到我們的張量中x。通過連接xcolx,我們可以根據(jù)我們剛剛預(yù)測(cè)的內(nèi)容進(jìn)入下一個(gè)標(biāo)記。這就是我們編寫自回歸的方法。

for i in range(num_return_sequences):tokens = x[i, :max_length].tolist()decoded = enc.decode(tokens)print(f">> {decoded}")

采樣循環(huán)完成后,我們可以遍歷每個(gè)選定的標(biāo)記并對(duì)其進(jìn)行解碼,向用戶顯示響應(yīng)。我們從i批處理中的第個(gè)標(biāo)記中抓取數(shù)據(jù)并對(duì)其進(jìn)行解碼以獲取下一個(gè)標(biāo)記。

如果您在我們的初始模型上運(yùn)行采樣循環(huán),您會(huì)注意到輸出有很多不足之處。這是因?yàn)槲覀冞€沒有訓(xùn)練任何權(quán)重。接下來的幾節(jié)課將展示如何開始對(duì)模型進(jìn)行簡(jiǎn)單的訓(xùn)練。

數(shù)據(jù)加載器精簡(jiǎn)版

所有訓(xùn)練都需要高質(zhì)量的數(shù)據(jù)。對(duì)于 Karpathy 的視頻,他喜歡使用公共領(lǐng)域的莎士比亞文本(更纖細(xì)的數(shù)據(jù)可在此處找到)。

class DataLoaderLite:def __init__(self, B, T):self.B = Bself.T = Twith open('shakespeare.txt', "r") as f:text = f.read()enc = tiktoken.get_encoding('gpt2')tokens = enc.encode(text)self.tokens = torch.tensor(tokens)print(f"1 epoch = {len(self.tokens) // B * T} batches")self.current_position = 0

我們首先簡(jiǎn)單地打開文件并讀入文本。此數(shù)據(jù)源僅為 ASCII,因此我們不必?fù)?dān)心任何意外的二進(jìn)制字符。我們使用tiktoken來獲取正文的編碼,然后將這些標(biāo)記轉(zhuǎn)換為張量。然后,我們創(chuàng)建一個(gè)名為的變量current_position,它將讓我們知道我們當(dāng)前正在從標(biāo)記張量中的哪個(gè)位置進(jìn)行訓(xùn)練(自然,這被初始化為開頭)。請(qǐng)注意,此類不是從繼承的nn.Module,主要是因?yàn)槲覀冞@里不需要該forward函數(shù)。與采樣循環(huán)的提示部分一樣,我們的 DataLoaderLite 類只需要生成形狀為 (B, T) 的張量。

class DataLoaderLite:
# ...def next_batch(self):B, T = self.B, self.Tbuf = self.tokens[self.current_position: self.current_position+(B*T + 1)]x = (buf[:-1]).view(B, T)y = (buf[1:]).view(B,T)self.current_position += B * Tif self.current_position + (B*T+1) > len(self.tokens):self.current_position = 0return x,y

在上面我們定義了next_batch幫助訓(xùn)練的函數(shù)。為了讓程序運(yùn)行得更快,我們喜歡批量運(yùn)行計(jì)算。我們使用 B 和 T 字段來確定我們將要訓(xùn)練的批次大小 (B) 和序列長(zhǎng)度 (T)。使用這些變量,我們創(chuàng)建一個(gè)緩沖區(qū)來保存我們將要訓(xùn)練的標(biāo)記,將維度設(shè)置為行 B 和列 T。請(qǐng)注意,我們從 讀取到current_positioncurrent_position + (B*T + 1)其中 +1 是為了確保我們擁有批次的所有基本事實(shí)值B*T。

然后,我們按照相同的方式設(shè)置模型輸入 (?x) 和預(yù)期輸出 ( )。是除最后一個(gè)字符之外的整個(gè)緩沖區(qū),是除第一個(gè)字符之外的整個(gè)緩沖區(qū)?;舅枷胧?#xff0c;給定令牌緩沖區(qū)中的第一個(gè)值,我們期望從模型中獲取令牌緩沖區(qū)中的第二個(gè)令牌。yxy

最后,我們更新current_position并返回xy

權(quán)重初始化

因?yàn)槲覀兲幚淼氖歉怕?#xff0c;所以我們想要為權(quán)重選取初始值,這樣就可能需要更少的訓(xùn)練次數(shù)才能正確計(jì)算。我們的_init_weights函數(shù)可以幫助我們做到這一點(diǎn),方法是用零或正態(tài)分布初始化權(quán)重。

class GPT(nn.Module):
# ...def _init_weights(self, module):# layer norm is by default set to what we want, no need to adjust itif isinstance(module, nn.Linear):std = 0.02if hasattr(module, "NANOGPT_SCALE_INIT"):std *= (2 * self.config.n_layer) ** -0.5 # 2 * for 2 additions (attention & mlp)torch.nn.init.normal_(module.weight, mean=0.0, std=std)# reasonable values are set based off a certain equationif module.bias is not None:torch.nn.init.zeros_(module.bias)elif isinstance(module, nn.Embedding):torch.nn.init.normal_(module.weight, mean=0.0, std=0.02 )

如果您還記得之前的內(nèi)容,我們將 GPT 類的每個(gè)字段都傳入了_init_weights,因此我們正在處理nn.Modules 。我們使用 Xavier 方法來初始化我們的權(quán)重,這意味著我們將采樣分布的標(biāo)準(zhǔn)差設(shè)置為1 / sqrt(hidden_layers)。您會(huì)注意到,在代碼中,我們經(jīng)常使用硬編碼的 0.02 作為標(biāo)準(zhǔn)差。雖然這看起來可能很隨意,但從下表中您可以看到,由于 GPT-2 使用的隱藏維度都大約為 0.02,因此這是一個(gè)很好的近似值。

查看代碼時(shí),我們首先檢查nn.Module正在操作的模塊屬于哪種子類型。

如果模塊是線性的,那么我們將檢查它是否是我們從MLPCasualSelfAttention類中投影的投影之一(通過檢查它是否設(shè)置了NANO_GPT_INIT標(biāo)志)。如果是,那么我們的 0.02 近似值將不起作用,因?yàn)檫@些模塊中的隱藏層數(shù)量正在增加(這是我們?cè)陬愔刑砑訌埩康暮瘮?shù)Block)。因此,GPT-2 論文使用縮放函數(shù)來解釋這一點(diǎn):1/sqrt(2 * self.config.n_layer)2*是因?yàn)槲覀兊?code>Block有 2 個(gè)地方我們要添加張量。

如果線性模塊中有偏差,我們將首先將它們?nèi)砍跏蓟癁榱恪?/span>

如果我們有一個(gè)Embedding模塊(如令牌或位置編碼部分),我們將使用相同的正態(tài)分布(標(biāo)準(zhǔn)差為 0.02)對(duì)其進(jìn)行初始化。

如果您還記得的話,我們的模型中還有另一種模塊子類型:nn.LayerNorm。此類已使用正態(tài)分布進(jìn)行了初始化,因此我們認(rèn)為這已經(jīng)足夠好了,不需要進(jìn)行任何更改。

訓(xùn)練循環(huán)

現(xiàn)在我們已經(jīng)設(shè)置了訓(xùn)練基礎(chǔ),讓我們組合一個(gè)快速訓(xùn)練循環(huán)來訓(xùn)練我們的模型。

device = "cpu"
if torch.cuda.is_available():device = "cuda"
elif hasattr(torch.backends, "mps") and torch.backends.mps.is_available():device = "mps"
print(f"device {device}")num_return_sequences = 5
max_length = 30torch.manual_seed(1337)train_loader = DataLoaderLite(B=4, T=32)model = GPT(GPTConfig())
model.to(device)

您可以看到,我們重復(fù)設(shè)備計(jì)算以獲得最佳性能。然后,我們將數(shù)據(jù)加載器設(shè)置為使用批處理大小 4 和序列長(zhǎng)度 32(任意設(shè)置,盡管 2 的冪對(duì)內(nèi)存效率最好)。

optimizer = torch.optim.AdamW(model.parameters(), lr=3e-4)
for i in range(50):x, y = train_loader.next_batch()x, y = x.to(device), y.to(device)optimizer.zero_grad() #have to start with a zero gradientlogits, loss = model(x, y)loss.backward() #adds to the gradient (+=, which is why they must start as 0)optimizer.step()print(f"loss {loss.item()}, step {i}")

現(xiàn)在我們有了優(yōu)化器,它將幫助我們訓(xùn)練模型。優(yōu)化器是一個(gè) PyTorch 類,它接收應(yīng)該訓(xùn)練的參數(shù)(在我們的例子中是從類中給出的參數(shù)GPT),然后接收學(xué)習(xí)率,它是訓(xùn)練期間的一個(gè)超參數(shù),決定了我們應(yīng)該以多快的速度調(diào)整參數(shù)——學(xué)習(xí)率越高意味著每次運(yùn)行后權(quán)重的變化越大。我們根據(jù) Karpathy 的建議選擇了我們的值。

然后我們使用 50 個(gè)訓(xùn)練步驟來訓(xùn)練模型。我們首先獲取訓(xùn)練批次并將其移動(dòng)到我們的設(shè)備上。我們將優(yōu)化器的梯度設(shè)置為零(pytorch 中的梯度是總和,因此如果我們不將其歸零,我們將從上一批中攜帶信息)。我們計(jì)算模型的 logits 和損失,然后運(yùn)行反向傳播以確定新的權(quán)重模型應(yīng)該是什么。最后,我們運(yùn)行optimizer.step()以更新我們所有的模型參數(shù)。

完整性檢查

要查看上述所有代碼的運(yùn)行情況,您可以查看我的 Google Colab,我將所有這些代碼組合起來并在 NVIDIA T4 GPU 上運(yùn)行。運(yùn)行我們的訓(xùn)練循環(huán),我們看到損失從 ~11 開始。為了進(jìn)行合理性測(cè)試,我們預(yù)計(jì)在開始時(shí)預(yù)測(cè)正確標(biāo)記的幾率是 (1/?vocab_size)。通過簡(jiǎn)化的損失函數(shù)-ln,我們得到 ~10.88,這正是我們開始的地方!

感謝關(guān)注雲(yún)閃世界。(亞馬遜aws和谷歌GCP服務(wù)協(xié)助解決云計(jì)算及產(chǎn)業(yè)相關(guān)解決方案)

?訂閱頻道(https://t.me/awsgoogvps_Host)
?TG交流群(t.me/awsgoogvpsHost)

http://m.risenshineclean.com/news/59266.html

相關(guān)文章:

  • 網(wǎng)站工作室 需要什么手續(xù)新媒體營(yíng)銷推廣方案
  • 512 做網(wǎng)站有做網(wǎng)站的嗎
  • 常見的企業(yè)網(wǎng)站有哪些百度店鋪怎么開通
  • 網(wǎng)站開發(fā) 實(shí)訓(xùn) 報(bào)告c++培訓(xùn)班學(xué)費(fèi)一般多少
  • 河南最新任命12個(gè)廳級(jí)360優(yōu)化大師官方下載手機(jī)
  • 建設(shè)境外網(wǎng)站推廣軟文范文800字
  • 柳城網(wǎng)站建設(shè)官網(wǎng)優(yōu)化哪家專業(yè)
  • 做威客有什么靠譜網(wǎng)站2022最好的百度seo
  • 出口貿(mào)易網(wǎng)站廣告營(yíng)銷的經(jīng)典案例
  • 網(wǎng)站建設(shè)的售后百度關(guān)鍵詞網(wǎng)站排名優(yōu)化軟件
  • 公司網(wǎng)站制作設(shè)上海網(wǎng)站制作公司
  • 網(wǎng)站開發(fā)畢設(shè)文獻(xiàn)網(wǎng)絡(luò)營(yíng)銷swot分析
  • 網(wǎng)站后臺(tái)php開發(fā)教程seo手機(jī)關(guān)鍵詞排行推廣
  • 網(wǎng)站 分析最新戰(zhàn)爭(zhēng)新聞事件今天
  • 第三方做的網(wǎng)站不給源代碼站長(zhǎng)工具如何使用
  • html怎么做網(wǎng)站設(shè)計(jì)以網(wǎng)絡(luò)營(yíng)銷為主題的論文
  • 做淘寶客網(wǎng)站教程短網(wǎng)址生成網(wǎng)站
  • 建湖營(yíng)銷型網(wǎng)站建設(shè)工作室銀川網(wǎng)站seo
  • 錦州網(wǎng)站建設(shè)品牌seo優(yōu)化需要多少錢
  • 個(gè)人網(wǎng)站制作網(wǎng)站2022國(guó)內(nèi)外重大新聞事件10條
  • 山東青島網(wǎng)站建設(shè)公司哪家專業(yè)制作網(wǎng)頁的流程步驟
  • 長(zhǎng)寧網(wǎng)站推廣公司網(wǎng)絡(luò)推廣渠道公司
  • 學(xué)做衣服網(wǎng) 繽紛網(wǎng)站查收錄網(wǎng)站
  • 江陰建設(shè)銀行網(wǎng)站全網(wǎng)自媒體平臺(tái)大全
  • 網(wǎng)站建設(shè)相關(guān)推薦網(wǎng)絡(luò)優(yōu)化工程師前景
  • 做網(wǎng)站聽的純音樂seo關(guān)鍵詞首頁排名代發(fā)
  • 網(wǎng)站開發(fā)工具 楓子科技谷歌代運(yùn)營(yíng)
  • 怎么制作網(wǎng)站seo公司優(yōu)化方案
  • wordpress自定義樣式什么是seo文章
  • 東莞手機(jī)網(wǎng)站制作公司鄭州網(wǎng)站顧問