1.GPT簡(jiǎn)介
????????GPT(Generative Pre-trained Transformer)模型是一種基于Transformer架構(gòu)的生成式預(yù)訓(xùn)練模型,由OpenAI開(kāi)發(fā)。它采用了無(wú)監(jiān)督學(xué)習(xí)的方式進(jìn)行預(yù)訓(xùn)練,然后通過(guò)微調(diào)適應(yīng)特定的任務(wù)。GPT模型的結(jié)構(gòu)由多層Transformer解碼器組成,每個(gè)解碼器由多頭自注意力機(jī)制和前饋神經(jīng)網(wǎng)絡(luò)組成。自注意力機(jī)制能夠?qū)斎氲男蛄羞M(jìn)行編碼,并捕捉序列中的上文關(guān)系,而前饋神經(jīng)網(wǎng)絡(luò)則負(fù)責(zé)對(duì)編碼后的向量進(jìn)行進(jìn)一步的非線性轉(zhuǎn)換。通過(guò)堆疊多個(gè)解碼器,GPT模型能夠?qū)W習(xí)到更加豐富的語(yǔ)義表示。
????????在預(yù)訓(xùn)練階段,GPT模型采用了大規(guī)模的無(wú)標(biāo)簽文本數(shù)據(jù),并根據(jù)上文來(lái)預(yù)測(cè)下一個(gè)詞。這個(gè)預(yù)測(cè)任務(wù)使模型能夠?qū)W習(xí)到語(yǔ)言的統(tǒng)計(jì)規(guī)律和語(yǔ)義信息。預(yù)訓(xùn)練采用了Transformer的自回歸結(jié)構(gòu),即每個(gè)位置的詞只能依賴于前面的詞。這種方式使得模型具有了生成文本的能力。
2018年6月 GPT-1:約5GB文本,1.17億參數(shù)量2019年2月 GPT-2:約40GB文本,15億參數(shù)量2020年5月 GPT-3:約45TB文本,1750億參數(shù)量
GPT又有哪些應(yīng)用呢?
1.1 文本生成:GPT可以通過(guò)學(xué)習(xí)大量文本數(shù)據(jù),從而生成新的文本。例如,可以用GPT來(lái)生成文章、故事、甚至是詩(shī)歌等。
1.2 對(duì)話系統(tǒng):GPT還可以用來(lái)構(gòu)建對(duì)話系統(tǒng),例如智能客服、聊天機(jī)器人等。用戶可以通過(guò)與GPT表達(dá)自己的意圖和需求,GPT可以自動(dòng)生成回復(fù)。
1.3 機(jī)器翻譯:GPT也可以用來(lái)進(jìn)行機(jī)器翻譯。通過(guò)學(xué)習(xí)不同語(yǔ)言的大量文本數(shù)據(jù),GPT可以將一種語(yǔ)言的文本轉(zhuǎn)換成另一種語(yǔ)言的文本。
1.4 情感分析:GPT可以用來(lái)進(jìn)行情感分析,例如對(duì)一段文本進(jìn)行情感分類,判斷其是正面、負(fù)面還是中性的。
1.5 問(wèn)答系統(tǒng):GPT還可以用來(lái)構(gòu)建問(wèn)答系統(tǒng),例如知識(shí)問(wèn)答、智能客服等。用戶可以通過(guò)向GPT提出問(wèn)題,GPT可以自動(dòng)生成回答。
2.GPT代碼實(shí)戰(zhàn)
2.1定義縮放點(diǎn)積注意力類
import numpy as np # 導(dǎo)入 numpy 庫(kù)
import torch # 導(dǎo)入 torch 庫(kù)
import torch.nn as nn # 導(dǎo)入 torch.nn 庫(kù)
d_k = 64 # K(=Q) 維度
d_v = 64 # V 維度
# 定義縮放點(diǎn)積注意力類
class ScaledDotProductAttention(nn.Module):
def __init__(self):
super(ScaledDotProductAttention, self).__init__()
def forward(self, Q, K, V, attn_mask):
#------------------------- 維度信息 --------------------------------
# Q K V [batch_size, n_heads, len_q/k/v, dim_q=k/v] (dim_q=dim_k)
# attn_mask [batch_size, n_heads, len_q, len_k]
#----------------------------------------------------------------
# 計(jì)算注意力分?jǐn)?shù)(原始權(quán)重)[batch_size,n_heads,len_q,len_k]
scores = torch.matmul(Q, K.transpose(-1, -2)) / np.sqrt(d_k)
#------------------------- 維度信息 --------------------------------
# scores [batch_size, n_heads, len_q, len_k]
#-----------------------------------------------------------------
# 使用注意力掩碼,將 attn_mask 中值為 1 的位置的權(quán)重替換為極小值
#------------------------- 維度信息 --------------------------------
# attn_mask [batch_size, n_heads, len_q, len_k], 形狀和 scores 相同
#-----------------------------------------------------------------
scores.masked_fill_(attn_mask, -1e9)
# 對(duì)注意力分?jǐn)?shù)進(jìn)行 softmax 歸一化
weights = nn.Softmax(dim=-1)(scores)
#------------------------- 維度信息 --------------------------------
# weights [batch_size, n_heads, len_q, len_k], 形狀和 scores 相同
#-----------------------------------------------------------------
# 計(jì)算上下文向量(也就是注意力的輸出), 是上下文信息的緊湊表示
context = torch.matmul(weights, V)
#------------------------- 維度信息 --------------------------------
# context [batch_size, n_heads, len_q, dim_v]
#-----------------------------------------------------------------
return context, weights # 返回上下文向量和注意力分?jǐn)?shù)
2.2定義多頭自注意力類
# 定義多頭自注意力類
d_embedding = 512 # Embedding 的維度
n_heads = 8 # Multi-Head Attention 中頭的個(gè)數(shù)
batch_size = 3 # 每一批的數(shù)據(jù)大小
class MultiHeadAttention(nn.Module):
def __init__(self):
super(MultiHeadAttention, self).__init__()
self.W_Q = nn.Linear(d_embedding, d_k * n_heads) # Q的線性變換層
self.W_K = nn.Linear(d_embedding, d_k * n_heads) # K的線性變換層
self.W_V = nn.Linear(d_embedding, d_v * n_heads) # V的線性變換層
self.linear = nn.Linear(n_heads * d_v, d_embedding)
self.layer_norm = nn.LayerNorm(d_embedding)
def forward(self, Q, K, V, attn_mask):
#------------------------- 維度信息 --------------------------------
# Q K V [batch_size, len_q/k/v, embedding_dim]
#-----------------------------------------------------------------
residual, batch_size = Q, Q.size(0) # 保留殘差連接
# 將輸入進(jìn)行線性變換和重塑,以便后續(xù)處理
q_s = self.W_Q(Q).view(batch_size, -1, n_heads, d_k).transpose(1,2)
k_s = self.W_K(K).view(batch_size, -1, n_heads, d_k).transpose(1,2)
v_s = self.W_V(V).view(batch_size, -1, n_heads, d_v).transpose(1,2)
#------------------------- 維度信息 --------------------------------
# q_s k_s v_s: [batch_size, n_heads, len_q/k/v, d_q=k/v]
#-----------------------------------------------------------------
# 將注意力掩碼復(fù)制到多頭 attn_mask: [batch_size, n_heads, len_q, len_k]
attn_mask = attn_mask.unsqueeze(1).repeat(1, n_heads, 1, 1)
#------------------------- 維度信息 --------------------------------
# attn_mask [batch_size, n_heads, len_q, len_k]
#-----------------------------------------------------------------
# 使用縮放點(diǎn)積注意力計(jì)算上下文和注意力權(quán)重
context, weights = ScaledDotProductAttention()(q_s, k_s, v_s, attn_mask)
#------------------------- 維度信息 --------------------------------
# context [batch_size, n_heads, len_q, dim_v]
# weights [batch_size, n_heads, len_q, len_k]
#-----------------------------------------------------------------
# 通過(guò)調(diào)整維度將多個(gè)頭的上下文向量連接在一起
context = context.transpose(1, 2).contiguous().view(batch_size, -1, n_heads * d_v)
#------------------------- 維度信息 --------------------------------
# context [batch_size, len_q, n_heads * dim_v]
#-----------------------------------------------------------------
# 用一個(gè)線性層把連接后的多頭自注意力結(jié)果轉(zhuǎn)換,原始地嵌入維度
output = self.linear(context)
#------------------------- 維度信息 --------------------------------
# output [batch_size, len_q, embedding_dim]
#-----------------------------------------------------------------
# 與輸入 (Q) 進(jìn)行殘差鏈接,并進(jìn)行層歸一化后輸出
output = self.layer_norm(output + residual)
#------------------------- 維度信息 --------------------------------
# output [batch_size, len_q, embedding_dim]
#-----------------------------------------------------------------
return output, weights # 返回層歸一化的輸出和注意力權(quán)重
2.3定義逐位置前饋網(wǎng)絡(luò)類
# 定義逐位置前饋網(wǎng)絡(luò)類
class PoswiseFeedForwardNet(nn.Module):
def __init__(self, d_ff=2048):
super(PoswiseFeedForwardNet, self).__init__()
# 定義一維卷積層 1,用于將輸入映射到更高維度
self.conv1 = nn.Conv1d(in_channels=d_embedding, out_channels=d_ff, kernel_size=1)
# 定義一維卷積層 2,用于將輸入映射回原始維度
self.conv2 = nn.Conv1d(in_channels=d_ff, out_channels=d_embedding, kernel_size=1)
# 定義層歸一化
self.layer_norm = nn.LayerNorm(d_embedding)
def forward(self, inputs):
#------------------------- 維度信息 --------------------------------
# inputs [batch_size, len_q, embedding_dim]
#----------------------------------------------------------------
residual = inputs # 保留殘差連接
# 在卷積層 1 后使用 ReLU 激活函數(shù)
output = nn.ReLU()(self.conv1(inputs.transpose(1, 2)))
#------------------------- 維度信息 --------------------------------
# output [batch_size, d_ff, len_q]
#----------------------------------------------------------------
# 使用卷積層 2 進(jìn)行降維
output = self.conv2(output).transpose(1, 2)
#------------------------- 維度信息 --------------------------------
# output [batch_size, len_q, embedding_dim]
#----------------------------------------------------------------
# 與輸入進(jìn)行殘差鏈接,并進(jìn)行層歸一化
output = self.layer_norm(output + residual)
#------------------------- 維度信息 --------------------------------
# output [batch_size, len_q, embedding_dim]
#----------------------------------------------------------------
return output # 返回加入殘差連接后層歸一化的結(jié)果
2.4生成正弦位置編碼表的函數(shù),用于在 Transformer 中引入位置信息?
# 生成正弦位置編碼表的函數(shù),用于在 Transformer 中引入位置信息
def get_sin_enc_table(n_position, embedding_dim):
#------------------------- 維度信息 --------------------------------
# n_position: 輸入序列的最大長(zhǎng)度
# embedding_dim: 詞嵌入向量的維度
#-----------------------------------------------------------------
# 根據(jù)位置和維度信息,初始化正弦位置編碼表
sinusoid_table = np.zeros((n_position, embedding_dim))
# 遍歷所有位置和維度,計(jì)算角度值
for pos_i in range(n_position):
for hid_j in range(embedding_dim):
angle = pos_i / np.power(10000, 2 * (hid_j // 2) / embedding_dim)
sinusoid_table[pos_i, hid_j] = angle
# 計(jì)算正弦和余弦值
sinusoid_table[:, 0::2] = np.sin(sinusoid_table[:, 0::2]) # dim 2i 偶數(shù)維
sinusoid_table[:, 1::2] = np.cos(sinusoid_table[:, 1::2]) # dim 2i+1 奇數(shù)維
#------------------------- 維度信息 --------------------------------
# sinusoid_table 的維度是 [n_position, embedding_dim]
#----------------------------------------------------------------
return torch.FloatTensor(sinusoid_table) # 返回正弦位置編碼表
2.5定義填充注意力掩碼函數(shù)?
# 定義填充注意力掩碼函數(shù)
def get_attn_pad_mask(seq_q, seq_k):
#------------------------- 維度信息 --------------------------------
# seq_q 的維度是 [batch_size, len_q]
# seq_k 的維度是 [batch_size, len_k]
#-----------------------------------------------------------------
batch_size, len_q = seq_q.size()
batch_size, len_k = seq_k.size()
# 生成布爾類型張量
pad_attn_mask = seq_k.data.eq(0).unsqueeze(1) # <PAD>token 的編碼值為 0
#------------------------- 維度信息 --------------------------------
# pad_attn_mask 的維度是 [batch_size,1,len_k]
#-----------------------------------------------------------------
# 變形為與注意力分?jǐn)?shù)相同形狀的張量
pad_attn_mask = pad_attn_mask.expand(batch_size, len_q, len_k)
#------------------------- 維度信息 --------------------------------
# pad_attn_mask 的維度是 [batch_size,len_q,len_k]
#-----------------------------------------------------------------
return pad_attn_mask # 返回填充位置的注意力掩碼
2.6定義編碼器層類?
# 定義編碼器層類
class EncoderLayer(nn.Module):
def __init__(self):
super(EncoderLayer, self).__init__()
self.enc_self_attn = MultiHeadAttention() # 多頭自注意力層
self.pos_ffn = PoswiseFeedForwardNet() # 位置前饋神經(jīng)網(wǎng)絡(luò)層
def forward(self, enc_inputs, enc_self_attn_mask):
#------------------------- 維度信息 --------------------------------
# enc_inputs 的維度是 [batch_size, seq_len, embedding_dim]
# enc_self_attn_mask 的維度是 [batch_size, seq_len, seq_len]
#-----------------------------------------------------------------
# 將相同的 Q,K,V 輸入多頭自注意力層 , 返回的 attn_weights 增加了頭數(shù)
enc_outputs, attn_weights = self.enc_self_attn(enc_inputs, enc_inputs,
enc_inputs, enc_self_attn_mask)
#------------------------- 維度信息 --------------------------------
# enc_outputs 的維度是 [batch_size, seq_len, embedding_dim]
# attn_weights 的維度是 [batch_size, n_heads, seq_len, seq_len]
# 將多頭自注意力 outputs 輸入位置前饋神經(jīng)網(wǎng)絡(luò)層
enc_outputs = self.pos_ffn(enc_outputs) # 維度與 enc_inputs 相同
#------------------------- 維度信息 --------------------------------
# enc_outputs 的維度是 [batch_size, seq_len, embedding_dim]
#-----------------------------------------------------------------
return enc_outputs, attn_weights # 返回編碼器輸出和每層編碼器注意力權(quán)重
2.7定義編碼器類
# 定義編碼器類
n_layers = 6 # 設(shè)置 Encoder 的層數(shù)
class Encoder(nn.Module):
def __init__(self, corpus):
super(Encoder, self).__init__()
self.src_emb = nn.Embedding(len(corpus.src_vocab), d_embedding) # 詞嵌入層
self.pos_emb = nn.Embedding.from_pretrained( \
get_sin_enc_table(corpus.src_len+1, d_embedding), freeze=True) # 位置嵌入層
self.layers = nn.ModuleList(EncoderLayer() for _ in range(n_layers))# 編碼器層數(shù)
def forward(self, enc_inputs):
#------------------------- 維度信息 --------------------------------
# enc_inputs 的維度是 [batch_size, source_len]
#-----------------------------------------------------------------
# 創(chuàng)建一個(gè)從 1 到 source_len 的位置索引序列
pos_indices = torch.arange(1, enc_inputs.size(1) + 1).unsqueeze(0).to(enc_inputs)
#------------------------- 維度信息 --------------------------------
# pos_indices 的維度是 [1, source_len]
#-----------------------------------------------------------------
# 對(duì)輸入進(jìn)行詞嵌入和位置嵌入相加 [batch_size, source_len,embedding_dim]
enc_outputs = self.src_emb(enc_inputs) + self.pos_emb(pos_indices)
#------------------------- 維度信息 --------------------------------
# enc_outputs 的維度是 [batch_size, seq_len, embedding_dim]
#-----------------------------------------------------------------
# 生成自注意力掩碼
enc_self_attn_mask = get_attn_pad_mask(enc_inputs, enc_inputs)
#------------------------- 維度信息 --------------------------------
# enc_self_attn_mask 的維度是 [batch_size, len_q, len_k]
#-----------------------------------------------------------------
enc_self_attn_weights = [] # 初始化 enc_self_attn_weights
# 通過(guò)編碼器層 [batch_size, seq_len, embedding_dim]
for layer in self.layers:
enc_outputs, enc_self_attn_weight = layer(enc_outputs, enc_self_attn_mask)
enc_self_attn_weights.append(enc_self_attn_weight)
#------------------------- 維度信息 --------------------------------
# enc_outputs 的維度是 [batch_size, seq_len, embedding_dim] 維度與 enc_inputs 相同
# enc_self_attn_weights 是一個(gè)列表,每個(gè)元素的維度是 [batch_size, n_heads, seq_len, seq_len]
#-----------------------------------------------------------------
return enc_outputs, enc_self_attn_weights # 返回編碼器輸出和編碼器注意力權(quán)重
2.8生成后續(xù)注意力掩碼的函數(shù),用于在多頭自注意力計(jì)算中忽略未來(lái)信息
# 生成后續(xù)注意力掩碼的函數(shù),用于在多頭自注意力計(jì)算中忽略未來(lái)信息
def get_attn_subsequent_mask(seq):
#------------------------- 維度信息 --------------------------------
# seq 的維度是 [batch_size, seq_len(Q)=seq_len(K)]
#-----------------------------------------------------------------
# 獲取輸入序列的形狀
attn_shape = [seq.size(0), seq.size(1), seq.size(1)]
#------------------------- 維度信息 --------------------------------
# attn_shape 是一個(gè)一維張量 [batch_size, seq_len(Q), seq_len(K)]
#-----------------------------------------------------------------
# 使用 numpy 創(chuàng)建一個(gè)上三角矩陣(triu = triangle upper)
subsequent_mask = np.triu(np.ones(attn_shape), k=1)
#------------------------- 維度信息 --------------------------------
# subsequent_mask 的維度是 [batch_size, seq_len(Q), seq_len(K)]
#-----------------------------------------------------------------
# 將 numpy 數(shù)組轉(zhuǎn)換為 PyTorch 張量,并將數(shù)據(jù)類型設(shè)置為 byte(布爾值)
subsequent_mask = torch.from_numpy(subsequent_mask).byte()
#------------------------- 維度信息 --------------------------------
# 返回的 subsequent_mask 的維度是 [batch_size, seq_len(Q), seq_len(K)]
#-----------------------------------------------------------------
return subsequent_mask # 返回后續(xù)位置的注意力掩碼
2.9定義解碼器層類
# 定義解碼器層類
class DecoderLayer(nn.Module):
def __init__(self):
super(DecoderLayer, self).__init__()
self.self_attn = MultiHeadAttention() # 多頭自注意力層
self.feed_forward = PoswiseFeedForwardNet() # 逐位置前饋網(wǎng)絡(luò)層
self.norm1 = nn.LayerNorm(d_embedding) # 第一個(gè)層歸一化
self.norm2 = nn.LayerNorm(d_embedding) # 第二個(gè)層歸一化
def forward(self, dec_inputs, attn_mask=None):
# 使用多頭自注意力處理輸入
attn_output, _ = self.self_attn(dec_inputs, dec_inputs, dec_inputs, attn_mask)
# 將注意力輸出與輸入相加并進(jìn)行第一個(gè)層歸一化
norm1_outputs = self.norm1(dec_inputs + attn_output)
# 將歸一化后的輸出輸入到位置前饋神經(jīng)網(wǎng)絡(luò)
ff_outputs = self.feed_forward(norm1_outputs)
# 將前饋神經(jīng)網(wǎng)絡(luò)輸出與第一次歸一化后的輸出相加并進(jìn)行第二個(gè)層歸一化
dec_outputs = self.norm2(norm1_outputs + ff_outputs)
return dec_outputs # 返回解碼器層輸出
2.10定義解碼器類
# 定義解碼器類
n_layers = 6 # 設(shè)置 Decoder 的層數(shù)
class Decoder(nn.Module):
def __init__(self, vocab_size, max_seq_len):
super(Decoder, self).__init__()
# 詞嵌入層(參數(shù)為詞典維度)
self.src_emb = nn.Embedding(vocab_size, d_embedding)
# 位置編碼層(參數(shù)為序列長(zhǎng)度)
self.pos_emb = nn.Embedding(max_seq_len, d_embedding)
# 初始化 N 個(gè)解碼器層
self.layers = nn.ModuleList([DecoderLayer() for _ in range(n_layers)])
def forward(self, dec_inputs):
# 創(chuàng)建位置信息
positions = torch.arange(len(dec_inputs), device=dec_inputs.device).unsqueeze(-1)
# 將詞嵌入與位置編碼相加
inputs_embedding = self.src_emb(dec_inputs) + self.pos_emb(positions)
# 生成自注意力掩碼
attn_mask = get_attn_subsequent_mask(inputs_embedding).to(device)
# 初始化解碼器輸入,這是第一層解碼器層的輸入
dec_outputs = inputs_embedding
for layer in self.layers:
# 將輸入數(shù)據(jù)傳遞給解碼器層,并返回解碼器層的輸出,作為下一層的輸入
dec_outputs = layer(dec_outputs, attn_mask)
return dec_outputs # 返回解碼器輸出
2.11定義 GPT 模型?
# 定義 GPT 模型
class GPT(nn.Module):
def __init__(self, vocab_size, max_seq_len):
super(GPT, self).__init__()
self.decoder = Decoder(vocab_size, max_seq_len) # 解碼器,用于學(xué)習(xí)文本生成能力
self.projection = nn.Linear(d_embedding, vocab_size) # 全連接層,輸出預(yù)測(cè)結(jié)果
def forward(self, dec_inputs):
dec_outputs = self.decoder(dec_inputs) # 將輸入數(shù)據(jù)傳遞給解碼器
logits = self.projection(dec_outputs) # 傳遞給全連接層以生成預(yù)測(cè)
return logits # 返回預(yù)測(cè)結(jié)果
2.12構(gòu)建語(yǔ)料庫(kù)?
# 構(gòu)建語(yǔ)料庫(kù)
from collections import Counter
class LanguageCorpus:
def __init__(self, sentences):
self.sentences = sentences
# 計(jì)算語(yǔ)言的最大句子長(zhǎng)度,并加 2 以容納特殊符號(hào) <sos> 和 <eos>
self.seq_len = max([len(sentence.split()) for sentence in sentences]) + 2
self.vocab = self.create_vocabulary() # 創(chuàng)建源語(yǔ)言和目標(biāo)語(yǔ)言的詞匯表
self.idx2word = {v: k for k, v in self.vocab.items()} # 創(chuàng)建索引到單詞的映射
def create_vocabulary(self):
vocab = {'<pad>': 0, '<sos>': 1, '<eos>': 2}
counter = Counter()
# 統(tǒng)計(jì)語(yǔ)料庫(kù)的單詞頻率
for sentence in self.sentences:
words = sentence.split()
counter.update(words)
# 創(chuàng)建詞匯表,并為每個(gè)單詞分配一個(gè)唯一的索引
for word in counter:
if word not in vocab:
vocab[word] = len(vocab)
return vocab
def make_batch(self, batch_size, test_batch=False):
input_batch, output_batch = [], [] # 初始化批數(shù)據(jù)
sentence_indices = torch.randperm(len(self.sentences))[:batch_size] # 隨機(jī)選擇句子索引
for index in sentence_indices:
sentence = self.sentences[index]
# 將句子轉(zhuǎn)換為索引序列
seq = [self.vocab['<sos>']] + [self.vocab[word] for word in sentence.split()] + [self.vocab['<eos>']]
seq += [self.vocab['<pad>']] * (self.seq_len - len(seq)) # 對(duì)序列進(jìn)行填充
# 將處理好的序列添加到批次中
input_batch.append(seq[:-1])
output_batch.append(seq[1:])
return torch.LongTensor(input_batch), torch.LongTensor(output_batch)
2.13預(yù)料處理
with open("lang.txt", "r") as file: # 從文件中讀入語(yǔ)料
sentences = [line.strip() for line in file.readlines()]
corpus = LanguageCorpus(sentences) # 創(chuàng)建語(yǔ)料庫(kù)
vocab_size = len(corpus.vocab) # 詞匯表大小
max_seq_len = corpus.seq_len # 最大句子長(zhǎng)度(用于設(shè)置位置編碼)
print(f" 語(yǔ)料庫(kù)詞匯表大小 : {vocab_size}") # 打印詞匯表大小
print(f" 最長(zhǎng)句子長(zhǎng)度 : {max_seq_len}") # 打印最大序列長(zhǎng)
2.14訓(xùn)練模型
import torch.optim as optim # 導(dǎo)入優(yōu)化器
device = "cuda" if torch.cuda.is_available() else "cpu" # 設(shè)置設(shè)備
model = GPT(vocab_size, max_seq_len).to(device) # 創(chuàng)建 GPT 模型實(shí)例
criterion = nn.CrossEntropyLoss() # 損失函數(shù)
optimizer = optim.Adam(model.parameters(), lr=0.0001) # 優(yōu)化器
epochs = 500 # 訓(xùn)練輪次
for epoch in range(epochs): # 訓(xùn)練 epochs 輪
optimizer.zero_grad() # 梯度清零
inputs, targets = corpus.make_batch(batch_size) # 創(chuàng)建訓(xùn)練數(shù)據(jù)
inputs, targets = inputs.to(device), targets.to(device)
outputs = model(inputs) # 獲取模型輸出
loss = criterion(outputs.view(-1, vocab_size), targets.view(-1)) # 計(jì)算損失
if (epoch + 1) % 100 == 0: # 打印損失
print(f"Epoch: {epoch + 1:04d} cost = {loss:.6f}")
loss.backward() # 反向傳播
optimizer.step() # 更新參數(shù)
2.15測(cè)試文本生成?
# 測(cè)試文本生成
def generate_text(model, input_str, max_len=50):
model.eval() # 將模型設(shè)置為評(píng)估(測(cè)試)模式,關(guān)閉 dropout 和 batch normalization 等訓(xùn)練相關(guān)的層
# 將輸入字符串中的每個(gè) token 轉(zhuǎn)換為其在詞匯表中的索引
input_tokens = [corpus.vocab[token] for token in input_str]
# 創(chuàng)建一個(gè)新列表,將輸入的 tokens 復(fù)制到輸出 tokens 中 , 目前只有輸入的詞
output_tokens = input_tokens.copy()
with torch.no_grad(): # 禁用梯度計(jì)算,以節(jié)省內(nèi)存并加速測(cè)試過(guò)程
for _ in range(max_len): # 生成最多 max_len 個(gè) tokens
# 將輸出的 token 轉(zhuǎn)換為 PyTorch 張量,并增加一個(gè)代表批次的維度 [1, len(output_tokens)]
inputs = torch.LongTensor(output_tokens).unsqueeze(0).to(device)
outputs = model(inputs) # 輸出 logits 形狀為 [1, len(output_tokens), vocab_size]
# 在最后一個(gè)維度上獲取 logits 中的最大值,并返回其索引(即下一個(gè) token)
_, next_token = torch.max(outputs[:, -1, :], dim=-1)
next_token = next_token.item() # 將張量轉(zhuǎn)換為 Python 整數(shù)
if next_token == corpus.vocab["<eos>"]:
break # 如果生成的 token 是 EOS(結(jié)束符),則停止生成過(guò)程
output_tokens.append(next_token) # 將生成的 tokens 添加到 output_tokens 列表
# 將輸出 tokens 轉(zhuǎn)換回文本字符串
output_str = " ".join([corpus.idx2word[token] for token in output_tokens])
return output_str
input_str = ["Python"] # 輸入一個(gè)詞:Python
generated_text = generate_text(model, input_str) # 模型跟著這個(gè)詞生成后續(xù)文本
print(" 生成的文本 :", generated_text) # 打印預(yù)測(cè)文本
3.總結(jié)?
注意:GPT只有解碼器部分,但是我全都定義了,大家順便復(fù)習(xí)一下,然后仔細(xì)看一下解碼器的結(jié)構(gòu)。
?????????GPT模型的訓(xùn)練分為兩個(gè)階段:預(yù)訓(xùn)練和微調(diào)。在預(yù)訓(xùn)練階段,GPT模型利用大規(guī)模文本數(shù)據(jù)進(jìn)行自監(jiān)督學(xué)習(xí),通過(guò)掩蓋輸入文本的一部分內(nèi)容,讓模型預(yù)測(cè)被掩蓋的部分。這個(gè)預(yù)測(cè)任務(wù)被稱為“掩碼語(yǔ)言模型”(Masked Language Modeling)。
????????在微調(diào)階段,GPT模型通過(guò)在特定任務(wù)上進(jìn)行有監(jiān)督的微調(diào)來(lái)利用其在預(yù)訓(xùn)練階段學(xué)到的語(yǔ)言知識(shí)。通過(guò)在少量標(biāo)注數(shù)據(jù)上進(jìn)行微調(diào),GPT模型可以適應(yīng)特定的任務(wù),如文本生成、文本分類、機(jī)器翻譯等。
????????然而,GPT模型也存在一些挑戰(zhàn)和限制。例如,由于是基于自動(dòng)回歸的方式生成文本,它可能面臨生成不準(zhǔn)確、重復(fù)、和不連貫的問(wèn)題。此外,GPT模型對(duì)訓(xùn)練數(shù)據(jù)的質(zhì)量和多樣性敏感,可能會(huì)受到輸入偏見(jiàn)和不準(zhǔn)確信息的影響。
注意“l(fā)ang.txt”內(nèi)容如下:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-817167.html
Python is a popular programming language.
I love to code in Python.
Data science is a hot topic in the tech industry.
Machine learning and deep learning are important parts of data science.
I am learning how to build neural networks.
Neural networks are modeled after the structure of the human brain.
Artificial intelligence has many applications in various industries.
Natural language processing is a branch of AI that deals with language understanding.
The rise of big data has led to an increased demand for data scientists.
I enjoy analyzing and visualizing data using Python libraries like Pandas and Matplotlib.
Data cleaning is an important part of data analysis.
I am fascinated by the power of deep learning algorithms.
Self-driving cars are an example of the practical applications of AI.
The tech industry is constantly evolving and changing.
I believe that AI will have a major impact on the future of work.
I am excited to see how AI will continue to develop and change the world.
The ethical implications of AI are a topic of much debate and discussion.
As with any powerful technology, there is a responsibility to use AI ethically and responsibly.
I think that the benefits of AI outweigh the risks, if used wisely.
Programming is a valuable skill in the digital age.文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-817167.html
到了這里,關(guān)于Python 基于pytorch從頭寫GPT模型;實(shí)現(xiàn)gpt實(shí)戰(zhàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!