GPT
GPT全稱為Generative Pre-trained Transformer,它使用了Transformer中的Decoder架構(gòu),并通過大規(guī)模的無監(jiān)督預(yù)訓(xùn)練來提高模型的表現(xiàn)力。在預(yù)訓(xùn)練階段,GPT通過處理大量的無標(biāo)注文本數(shù)據(jù),學(xué)習(xí)到了語言的統(tǒng)計(jì)規(guī)律和上下文信息。在實(shí)際應(yīng)用時(shí),GPT可以通過微調(diào)(fine-tuning)的方式,根據(jù)具體任務(wù)的需求,對(duì)預(yù)訓(xùn)練的模型進(jìn)行微小的調(diào)整,從而適應(yīng)不同的文本生成、問答等任務(wù)。GPT在自然語言生成和處理任務(wù)上表現(xiàn)出色,在多個(gè)公開數(shù)據(jù)集上都取得了很好的成績(jī)。
無監(jiān)督的預(yù)訓(xùn)練
GPT的無監(jiān)督預(yù)訓(xùn)練就是讓模型自己學(xué)習(xí)語言的規(guī)律和上下文信息,而無需人為標(biāo)注數(shù)據(jù)。在預(yù)訓(xùn)練階段,GPT使用了大量的無標(biāo)注文本數(shù)據(jù)進(jìn)行訓(xùn)練,例如維基百科、互聯(lián)網(wǎng)新聞等大規(guī)模語料庫。GPT將這些文本數(shù)據(jù)輸入到模型中,通過不斷地學(xué)習(xí)語言中的統(tǒng)計(jì)規(guī)律和上下文信息,提高模型的表現(xiàn)力。
在這個(gè)階段中,GPT最早期使用的是一種基于自回歸模型的語言模型,它通過最大化給定輸入序列的下一個(gè)單詞出現(xiàn)的概率來預(yù)訓(xùn)練模型。
自回歸模型的目標(biāo)是最大化模型對(duì)無標(biāo)注文本數(shù)據(jù)的似然性,即最大化模型在給定無標(biāo)注文本數(shù)據(jù)下的對(duì)數(shù)似然函數(shù)。我們希望訓(xùn)練出來的模型可以在當(dāng)前輸入文本序列的基礎(chǔ)上,預(yù)測(cè)下一個(gè)單詞出現(xiàn)的概率。而預(yù)測(cè)概率的一個(gè)重要指標(biāo)就是似然性,即當(dāng)前模型預(yù)測(cè)的結(jié)果與實(shí)際觀測(cè)值之間的相似程度。假設(shè)我們有一個(gè)無標(biāo)注文本數(shù)據(jù)集D = {x_1, x_2, ..., x_N},其中每個(gè)x_i是一個(gè)長(zhǎng)度為T_i的文本序列,而模型的參數(shù)為\theta。假設(shè)我們的模型能夠?qū)_i中的每個(gè)單詞表示為{w_{i,1}, w_{i,2}, ..., w_{i,T_i}},那么模型對(duì)于x_i的對(duì)數(shù)似然函數(shù)可以表示為:
\log p(x_i|\theta)=\sum_{t=1}^{T_i}\log p(w_{i,t}|w_{i,<t},\theta)
其中,p(w_{i,t}|w_{i, <t},\theta)表示給定上文w_{i, <t}的情況下,模型對(duì)于w_{i,t}的條件概率分布。
在GPT2,GPT3中在預(yù)訓(xùn)練階段還引入了掩碼語言模型(MLM,Masked Language Model,和Bert中的一樣)
MLM的目標(biāo)是在輸入序列中隨機(jī)遮蓋一些單詞,并讓模型預(yù)測(cè)這些被遮蓋的單詞。
掩碼語言模型(Masked Language Model,MLM)的似然函數(shù)表示為:
L_{MLM}=\prod_{i=1}^{N}P(w_{i}|w_{<i},w_{>i})
其中,w_{i}表示第i個(gè)位置的被遮蔽的單詞,通常在文本中用一個(gè)特殊符號(hào)“[MASK]”標(biāo)記,w_{<i}表示第i個(gè)位置之前的單詞序列,w_{>i}表示第i個(gè)位置之后的單詞序列,N表示文本序列的長(zhǎng)度。這些都是通過多層級(jí)聯(lián)的Transformer的decoder實(shí)現(xiàn)的。通過梯度下降的訓(xùn)練方法,可以使得似然函數(shù)最大。
有監(jiān)督的微調(diào)
GPT中的Supervised fine-tuning是指在完成了無監(jiān)督的預(yù)訓(xùn)練后,使用有標(biāo)注數(shù)據(jù)對(duì)模型進(jìn)行有監(jiān)督的微調(diào),以適應(yīng)特定的下游任務(wù)。

假設(shè)我們有一個(gè)已經(jīng)預(yù)訓(xùn)練好的GPT模型,它的參數(shù)為\theta?,F(xiàn)在,我們想將這個(gè)模型應(yīng)用于一個(gè)下游任務(wù),例如文本分類任務(wù)。在文本分類任務(wù)中,我們有一個(gè)由N個(gè)樣本組成的訓(xùn)練集,其中第i個(gè)樣本的輸入為x_i,對(duì)應(yīng)的標(biāo)簽為y_i。
在進(jìn)行Supervised fine-tuning時(shí),我們需要對(duì)GPT模型進(jìn)行微調(diào),以適應(yīng)特定的下游任務(wù)。我們可以將GPT模型的輸出層進(jìn)行修改,例如添加一個(gè)全連接層,并將其連接到GPT模型的最后一個(gè)隱藏層。我們可以將這個(gè)修改后的模型表示為GPT_{\text{ft}}(\cdot;\theta_{\text{ft}}),其中\(zhòng)theta_{\text{ft}}是微調(diào)后的參數(shù)。
對(duì)于文本分類任務(wù),我們可以定義一個(gè)損失函數(shù)L_{\text{cls}},cls代表輸入的開端,損失函數(shù)用于衡量模型在分類任務(wù)上的性能。常見的損失函數(shù)包括交叉熵?fù)p失和均方誤差損失等。我們的目標(biāo)是最小化損失函數(shù)L_{\text{cls}},以適應(yīng)特定的下游任務(wù)。我們可以通過以下步驟來實(shí)現(xiàn)Supervised fine-tuning:
將預(yù)訓(xùn)練好的GPT模型的輸出層進(jìn)行修改,得到修改后的模型GPT_{\text{ft}}(\cdot;\theta_{\text{ft}})。
在訓(xùn)練集上對(duì)修改后的模型進(jìn)行訓(xùn)練,這里和預(yù)訓(xùn)練的文本集合不同,F(xiàn)ine-Tuning使用的是帶有標(biāo)簽的數(shù)據(jù)集,如情感分類、文本生成、問答等任務(wù)的標(biāo)注數(shù)據(jù)集,而預(yù)訓(xùn)練的集合是無標(biāo)簽的。最小化損失函數(shù)L_{\text{cls}}??梢允褂秒S機(jī)梯度下降等優(yōu)化算法進(jìn)行訓(xùn)練。
微調(diào)完成后,使用測(cè)試集對(duì)模型進(jìn)行評(píng)估,并計(jì)算模型在下游任務(wù)上的性能指標(biāo),例如準(zhǔn)確率、F1值等。
Supervised fine-tuning的數(shù)學(xué)表示可以如下表示:
\min_{\theta_{\mathrm{ft}}}\frac{1}{N}\sum_{i=1}^N L_{\mathrm{cls}}(GPT_{\mathrm{ft}}(x_i;\theta_{\mathrm{ft}}),y_i)\quad\quad\text{}
其中,L_{\text{cls}}(\cdot, \cdot)表示分類任務(wù)的損失函數(shù),x_i表示第i個(gè)樣本的輸入,y_i表示第i個(gè)樣本的標(biāo)簽。我們的目標(biāo)是找到微調(diào)后的參數(shù)\theta_{\text{ft}},使得模型在訓(xùn)練集上的損失函數(shù)最小。
在 Improving Language Understanding by Generative Pre-Training 這篇論文中,作者提出了一種自適應(yīng)的學(xué)習(xí)率策略,用于在 GPT 中進(jìn)行訓(xùn)練。訓(xùn)練的過程中只用到了12層的decoder網(wǎng)絡(luò)。
GPT的pytorch實(shí)現(xiàn)
首先,需要導(dǎo)入需要用到的庫和模塊:
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="python" cid="n33" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">import torch
import torch.nn as nn
from torch.nn import functional as F
</pre>
接下來,定義GPT模型的主要組成部分——Transformer Decoder。這里我們參考GPT-2,使用12個(gè)Transformer Decoder來構(gòu)建整個(gè)模型。在每個(gè)Transformer Decoder中,都包含一個(gè)多頭自注意力機(jī)制(multi-head self-attention),一個(gè)前饋神經(jīng)網(wǎng)絡(luò)(feedforward neural network)和一個(gè)殘差連接(residual connection):
class TransformerDecoder(nn.Module):
def __init__(self, hidden_dim, num_heads, ff_dim, dropout):
super().__init__()
self.multihead_attn = nn.MultiheadAttention(hidden_dim, num_heads)
self.dropout1 = nn.Dropout(dropout)
self.layer_norm1 = nn.LayerNorm(hidden_dim)
self.ff = nn.Sequential(
nn.Linear(hidden_dim, ff_dim),
nn.ReLU(),
nn.Dropout(dropout),
nn.Linear(ff_dim, hidden_dim),
)
self.dropout2 = nn.Dropout(dropout)
self.layer_norm2 = nn.LayerNorm(hidden_dim)
def forward(self, x, mask):
# Multi-head self-attention
attn_out, _ = self.multihead_attn(x, x, x, attn_mask=mask)
attn_out = self.dropout1(attn_out)
x = self.layer_norm1(x + attn_out)
# Feedforward neural network
ff_out = self.ff(x)
ff_out = self.dropout2(ff_out)
x = self.layer_norm2(x + ff_out)
return x
接下來,我們將這些Transformer Decoder串聯(lián)起來,形成整個(gè)GPT模型:
class GPT(nn.Module):
def __init__(self, num_tokens, hidden_dim, num_heads, num_layers, seq_len, dropout):
super().__init__()
self.token_emb = nn.Embedding(num_tokens, hidden_dim)
self.pos_emb = nn.Parameter(torch.zeros(1, seq_len, hidden_dim))
self.dropout = nn.Dropout(dropout)
self.decoders = nn.ModuleList([
TransformerDecoder(hidden_dim, num_heads, hidden_dim * 4, dropout)
for _ in range(num_layers)
])
self.output_layer = nn.Linear(hidden_dim, num_tokens)
self.softmax = nn.Softmax(dim=-1)
def forward(self, x):
# Token embeddings
x = self.token_emb(x)
# Add position embeddings
x += self.pos_emb[:, :x.shape[1]]
# Transformer Decoder layers
mask = torch.triu(torch.ones(x.shape[1], x.shape[1]), diagonal=1).bool().to(x.device)
for decoder in self.decoders:
x = decoder(x, mask)
# Output layer
x = self.output_layer(x)
x = self.softmax(x)
return x
最后,我們可以定義訓(xùn)練過程,包括損失函數(shù)、優(yōu)化器等:
GPT(num_tokens, hidden_dim, num_heads, num_layers, seq_len, dropout)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=lr)
for epoch in range(num_epochs):
for inputs, labels in dat