🚀 Melatih Model GPT dari Awal dengan PyTorch: Tokenizer + Transformer + Inferensi
Apakah Anda ingin membangun model GPT Anda sendiri dari awal? Dalam postingan blog ini, kita akan mempelajari langkah demi langkah cara melatih model GPT (Generative Pre-trained Transformer) menggunakan PyTorch. Kami akan membahas tokenisasi, arsitektur Transformer, dan inferensi. Panduan komprehensif ini ditujukan bagi para pemula dan praktisi berpengalaman yang ingin mendapatkan pemahaman mendalam tentang cara kerja model GPT dan menerapkannya dalam PyTorch.
Mengapa Melatih Model GPT dari Awal?
Meskipun ada banyak model GPT yang sudah dilatih sebelumnya yang tersedia, melatih model dari awal menawarkan beberapa keuntungan:
- Kustomisasi: Anda dapat menyesuaikan model dengan kumpulan data dan tugas khusus Anda.
- Pemahaman: Anda mendapatkan pemahaman mendalam tentang arsitektur dan pelatihan Transformer.
- Kontrol: Anda memiliki kontrol penuh atas arsitektur model, proses pelatihan, dan implementasi.
- Optimasi: Anda dapat mengoptimalkan model untuk sumber daya dan kebutuhan perangkat keras tertentu.
- Pembelajaran: Proses ini adalah pengalaman belajar yang luar biasa yang akan meningkatkan pemahaman Anda tentang pembelajaran mendalam.
Prasyarat
Sebelum kita mulai, pastikan Anda memiliki prasyarat berikut:
- Python: Pengetahuan dasar tentang Python diperlukan.
- PyTorch: Anda harus menginstal PyTorch. Anda dapat menginstalnya dari situs web PyTorch.
- Pemahaman Pembelajaran Mendalam: Familiaritas dengan konsep pembelajaran mendalam seperti backpropagation, penurunan gradien, dan jaringan saraf dianjurkan.
- Akses GPU (Opsional): GPU akan mempercepat proses pelatihan secara signifikan.
Kerangka Kerja Posting Blog
- Pengantar
- Mengapa melatih model GPT dari awal?
- Prasyarat
- Langkah 1: Persiapan Data
- Mengumpulkan dan membersihkan data
- Membuat vocabulary (kosakata)
- Langkah 2: Implementasi Tokenizer
- Subword Tokenization (Byte-Pair Encoding – BPE)
- Implementasi Tokenizer dengan Python
- Langkah 3: Membangun Arsitektur Transformer
- Encoder dan Decoder (hanya Decoder untuk GPT)
- Multi-Head Attention
- Feed Forward Network
- Layer Normalization dan Residual Connections
- Implementasi dengan PyTorch
- Langkah 4: Pelatihan Model
- Mempersiapkan Data untuk Pelatihan (Batching)
- Loss Function (Cross-Entropy)
- Optimizer (AdamW)
- Training Loop
- Langkah 5: Inferensi
- Generasi Teks
- Sampling Strategies (Greedy, Temperature Sampling)
- Implementasi Inferensi
- Langkah 6: Evaluasi dan Tuning
- Perplexity
- Evaluasi Kualitatif
- Hyperparameter Tuning
- Kesimpulan
- Ringkasan
- Langkah Selanjutnya
Langkah 1: Persiapan Data
Persiapan data adalah langkah penting dalam melatih model GPT. Kualitas data Anda akan secara langsung memengaruhi kinerja model Anda. Proses ini melibatkan pengumpulan, pembersihan, dan persiapan data agar sesuai untuk pelatihan.
Mengumpulkan dan Membersihkan Data
Sumber data dapat sangat bervariasi tergantung pada tujuan Anda. Beberapa sumber umum meliputi:
- Teks berbasis web: Wikipedia, Common Crawl, artikel berita.
- Buku: Project Gutenberg, perpustakaan digital.
- Kumpulan data khusus: Data dari aplikasi tertentu, misalnya, transkrip obrolan pelanggan, kode, atau teks ilmiah.
Setelah Anda mengumpulkan data Anda, langkah selanjutnya adalah membersihkannya. Pembersihan mungkin melibatkan:
- Menghapus HTML atau markup lain: Hilangkan semua tag HTML dan format yang tidak perlu.
- Menangani karakter khusus: Konversi atau hapus karakter khusus yang mungkin menyebabkan masalah.
- Koreksi kesalahan ejaan: Perbaiki kesalahan ejaan untuk meningkatkan kualitas data.
- Menghapus teks yang tidak relevan: Hilangkan teks yang tidak sesuai dengan tujuan Anda.
Contoh membersihkan data dengan Python:
import re
def clean_text(text):
# Remove HTML tags
text = re.sub(r'<[^>]+>', '', text)
# Remove special characters
text = re.sub(r'[^a-zA-Z0-9\s]', '', text)
# Convert to lowercase
text = text.lower()
return text
# Example usage
text = "This is a Sample Text!
With some HTML tags.
"
cleaned_text = clean_text(text)
print(cleaned_text) # Output: this is a sample text with some html tags
Membuat Vocabulary (Kosakata)
Vocabulary adalah daftar semua token unik (kata-kata atau subkata) dalam kumpulan data Anda. Ini adalah komponen penting dari model GPT, karena ia memetakan token ke indeks numerik, yang digunakan model untuk memproses teks.
- Menghitung Frekuensi Token: Hitung frekuensi setiap token dalam kumpulan data Anda.
- Membuat Vocabulary: Pilih sejumlah token yang paling sering muncul untuk disertakan dalam vocabulary Anda.
- Menambahkan Token Khusus: Tambahkan token khusus seperti:
<PAD>
: Untuk padding urutan agar memiliki panjang yang sama.<UNK>
: Untuk token yang tidak ada dalam vocabulary.<BOS>
: Awal dari urutan.<EOS>
: Akhir dari urutan.
- Membuat Pemetaan: Buat pemetaan antara token dan indeks mereka (
token2id
) dan indeks ke token (id2token
).
Contoh membuat vocabulary dengan Python:
from collections import Counter
def build_vocabulary(text, vocab_size=10000):
# Split text into tokens
tokens = text.split()
# Count token frequencies
token_counts = Counter(tokens)
# Get the most common tokens
most_common_tokens = [token for token, count in token_counts.most_common(vocab_size - 4)]
# Add special tokens
vocabulary = ['<PAD>', '<UNK>', '<BOS>', '<EOS>'] + most_common_tokens
# Create token to index mapping
token2id = {token: idx for idx, token in enumerate(vocabulary)}
# Create index to token mapping
id2token = {idx: token for idx, token in enumerate(vocabulary)}
return vocabulary, token2id, id2token
# Example usage
text = "this is a sample text this is a sample text this is a sample"
vocabulary, token2id, id2token = build_vocabulary(text)
print("Vocabulary:", vocabulary)
print("Token to ID:", token2id)
print("ID to Token:", id2token)
Langkah 2: Implementasi Tokenizer
Tokenizer bertugas untuk memecah teks mentah menjadi token yang dapat diproses oleh model. Teknik tokenisasi umum untuk model GPT adalah Subword Tokenization, terutama Byte-Pair Encoding (BPE).
Subword Tokenization (Byte-Pair Encoding – BPE)
BPE adalah algoritma tokenisasi data yang mempelajari cara menggabungkan karakter menjadi token. Ia memulai dengan vocabulary dasar karakter dan secara iteratif menggabungkan pasangan karakter yang paling sering terjadi hingga vocabulary mencapai ukuran yang diinginkan.
- Inisialisasi: Mulai dengan vocabulary dasar karakter.
- Iterasi:
- Hitung frekuensi pasangan token yang berdekatan.
- Gabungkan pasangan yang paling sering terjadi ke dalam token baru.
- Tambahkan token baru ke vocabulary.
- Ulangi: Ulangi langkah iterasi hingga vocabulary mencapai ukuran yang diinginkan.
Contoh BPE:
Misalkan kita memiliki teks “low lower newest widest” dan kita ingin membuat vocabulary dengan ukuran 10.
- Inisialisasi: Vocabulary awal adalah
{'l', 'o', 'w', 'e', 'r', 'n', 's', 't', 'i', 'd'}
- Iterasi 1: Pasangan yang paling sering terjadi adalah ‘e’ ‘s’ (muncul dua kali). Gabungkan menjadi ‘es’. Vocabulary menjadi
{'l', 'o', 'w', 'e', 'r', 'n', 's', 't', 'i', 'd', 'es'}
- Iterasi 2: Pasangan yang paling sering terjadi adalah ‘es’ ‘t’ (muncul dua kali). Gabungkan menjadi ‘est’. Vocabulary menjadi
{'l', 'o', 'w', 'e', 'r', 'n', 's', 't', 'i', 'd', 'es', 'est'}
- Lanjutkan: Ulangi proses hingga ukuran vocabulary mencapai 10 (atau ukuran yang diinginkan).
Implementasi Tokenizer dengan Python
Implementasi BPE dari awal bisa jadi kompleks. Untungnya, ada pustaka yang tersedia yang menyederhanakan prosesnya, seperti tokenizers
dari Hugging Face.
from tokenizers import ByteLevelBPETokenizer
# Initialize a tokenizer
tokenizer = ByteLevelBPETokenizer()
# Customize training
tokenizer.train(
files=["path/to/your/data.txt"],
vocab_size=10000,
min_frequency=2,
special_tokens=["<PAD>", "<UNK>", "<BOS>", "<EOS>"]
)
# Save the tokenizer
tokenizer.save_model("path/to/save/tokenizer")
# Load the tokenizer
from tokenizers.implementations import ByteLevelBPETokenizer
tokenizer = ByteLevelBPETokenizer(
"path/to/save/tokenizer/vocab.json",
"path/to/save/tokenizer/merges.txt"
)
# Example usage
encoded = tokenizer.encode("This is a sample text.")
print("Encoded:", encoded.ids)
decoded = tokenizer.decode(encoded.ids)
print("Decoded:", decoded)
Langkah 3: Membangun Arsitektur Transformer
Arsitektur Transformer adalah inti dari model GPT. Arsitektur ini didasarkan pada mekanisme self-attention, yang memungkinkannya untuk mempertimbangkan hubungan antara semua kata dalam urutan.
Encoder dan Decoder (hanya Decoder untuk GPT)
Arsitektur Transformer asli memiliki komponen encoder dan decoder. Namun, model GPT hanya menggunakan tumpukan decoder Transformer. Decoder bertugas untuk menghasilkan teks, dengan mempertimbangkan input dan konteks yang sebelumnya dihasilkan.
Multi-Head Attention
Multi-Head Attention memungkinkan model untuk menghadiri bagian yang berbeda dari input secara bersamaan. Ini melibatkan proyeksi input ke dalam beberapa ruang representasi (head) dan menghitung perhatian secara paralel di setiap ruang.
Rumus untuk Attention:
Attention(Q, K, V) = softmax((Q * K^T) / sqrt(d_k)) * V
Q
: QueriesK
: KeysV
: Valuesd_k
: Dimensi dari keys
Multi-Head Attention melakukan proses ini beberapa kali (dengan “heads” yang berbeda) dan menggabungkan hasilnya.
Feed Forward Network
Feed Forward Network adalah jaringan saraf feed-forward yang diterapkan secara independen ke setiap posisi. Ini biasanya terdiri dari dua lapisan linier dengan fungsi aktivasi ReLU di antaranya.
Layer Normalization dan Residual Connections
Layer Normalization menstabilkan proses pelatihan dan mempercepat konvergensi. Residual connections (skip connections) membantu mengatasi masalah gradien menghilang dengan memungkinkan gradien untuk mengalir langsung melalui jaringan.
Implementasi dengan PyTorch
Berikut adalah implementasi PyTorch dari blok Decoder Transformer:
import torch
import torch.nn as nn
import torch.nn.functional as F
class MultiHeadAttention(nn.Module):
def __init__(self, d_model, num_heads):
super(MultiHeadAttention, self).__init__()
self.d_model = d_model
self.num_heads = num_heads
self.d_k = d_model // num_heads
self.W_q = nn.Linear(d_model, d_model)
self.W_k = nn.Linear(d_model, d_model)
self.W_v = nn.Linear(d_model, d_model)
self.W_o = nn.Linear(d_model, d_model)
def scaled_dot_product_attention(self, Q, K, V, mask=None):
attn_scores = torch.matmul(Q, K.transpose(-2, -1)) / torch.sqrt(torch.tensor(self.d_k, dtype=torch.float32))
if mask is not None:
attn_scores = attn_scores.masked_fill(mask == 0, float('-inf'))
attn_probs = F.softmax(attn_scores, dim=-1)
output = torch.matmul(attn_probs, V)
return output
def split_heads(self, x):
batch_size, seq_length, d_model = x.size()
return x.view(batch_size, seq_length, self.num_heads, self.d_k).transpose(1, 2)
def combine_heads(self, x):
batch_size, _, seq_length, d_k = x.size()
return x.transpose(1, 2).contiguous().view(batch_size, seq_length, self.d_model)
def forward(self, Q, K, V, mask=None):
Q = self.W_q(Q)
K = self.W_k(K)
V = self.W_v(V)
Q = self.split_heads(Q)
K = self.split_heads(K)
V = self.split_heads(V)
attn_output = self.scaled_dot_product_attention(Q, K, V, mask)
output = self.combine_heads(attn_output)
output = self.W_o(output)
return output
class FeedForwardNetwork(nn.Module):
def __init__(self, d_model, d_ff):
super(FeedForwardNetwork, self).__init__()
self.linear1 = nn.Linear(d_model, d_ff)
self.linear2 = nn.Linear(d_ff, d_model)
def forward(self, x):
x = F.relu(self.linear1(x))
x = self.linear2(x)
return x
class TransformerBlock(nn.Module):
def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
super(TransformerBlock, self).__init__()
self.attn = MultiHeadAttention(d_model, num_heads)
self.feed_forward = FeedForwardNetwork(d_model, d_ff)
self.layer_norm1 = nn.LayerNorm(d_model)
self.layer_norm2 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout)
def forward(self, x, mask=None):
attn_output = self.attn(x, x, x, mask)
x = self.layer_norm1(x + self.dropout(attn_output))
ff_output = self.feed_forward(x)
x = self.layer_norm2(x + self.dropout(ff_output))
return x
class GPT(nn.Module):
def __init__(self, vocab_size, d_model, num_layers, num_heads, d_ff, max_seq_length, dropout=0.1):
super(GPT, self).__init__()
self.embedding = nn.Embedding(vocab_size, d_model)
self.positional_encoding = torch.zeros(max_seq_length, d_model)
pos = torch.arange(0, max_seq_length).unsqueeze(1)
div_term = torch.exp(torch.arange(0, d_model, 2) * (-torch.log(torch.tensor(10000.0)) / d_model))
self.positional_encoding[:, 0::2] = torch.sin(pos * div_term)
self.positional_encoding[:, 1::2] = torch.cos(pos * div_term)
self.positional_encoding = self.positional_encoding.unsqueeze(0)
self.transformer_blocks = nn.ModuleList([
TransformerBlock(d_model, num_heads, d_ff, dropout) for _ in range(num_layers)
])
self.layer_norm = nn.LayerNorm(d_model)
self.linear = nn.Linear(d_model, vocab_size)
def forward(self, x, mask=None):
batch_size, seq_length = x.size()
x = self.embedding(x)
x = x + self.positional_encoding[:, :seq_length, :]
for block in self.transformer_blocks:
x = block(x, mask)
x = self.layer_norm(x)
x = self.linear(x)
return x
Langkah 4: Pelatihan Model
Pelatihan model GPT melibatkan pemberian makan data ke model, menghitung loss, dan memperbarui bobot model menggunakan backpropagation.
Mempersiapkan Data untuk Pelatihan (Batching)
Data harus di batch agar dapat dilatih secara efisien. Batching melibatkan pengelompokan beberapa urutan menjadi satu tensor. Karena urutan mungkin memiliki panjang yang berbeda, padding digunakan untuk memastikan bahwa semua urutan dalam batch memiliki panjang yang sama.
Loss Function (Cross-Entropy)
Loss function mengukur perbedaan antara keluaran model dan target yang benar. Untuk model GPT, loss function yang umum digunakan adalah Cross-Entropy Loss.
Optimizer (AdamW)
Optimizer memperbarui bobot model berdasarkan gradien loss function. AdamW adalah optimizer yang umum digunakan yang sering memberikan hasil yang baik.
Training Loop
Training loop melibatkan iterasi atas data pelatihan, menghitung loss, dan memperbarui bobot model.
import torch.optim as optim
from torch.utils.data import DataLoader, Dataset
# Assume you have a list of tokenized sequences called 'tokenized_data'
# and token2id, id2token from previous steps.
class TextDataset(Dataset):
def __init__(self, tokenized_data, max_seq_length):
self.tokenized_data = tokenized_data
self.max_seq_length = max_seq_length
def __len__(self):
return len(self.tokenized_data)
def __getitem__(self, idx):
sequence = self.tokenized_data[idx]
# Pad or truncate sequence
if len(sequence) < self.max_seq_length:
sequence = sequence + [token2id['<PAD>']] * (self.max_seq_length - len(sequence))
else:
sequence = sequence[:self.max_seq_length]
sequence = torch.tensor(sequence)
return sequence
# Hyperparameters
vocab_size = len(vocabulary)
d_model = 256
num_layers = 4
num_heads = 8
d_ff = 1024
max_seq_length = 128
dropout = 0.1
batch_size = 32
learning_rate = 0.0001
num_epochs = 10
# Create the model
model = GPT(vocab_size, d_model, num_layers, num_heads, d_ff, max_seq_length, dropout)
# Create the dataset and dataloader
dataset = TextDataset(tokenized_data, max_seq_length)
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)
# Define the loss function and optimizer
criterion = nn.CrossEntropyLoss(ignore_index=token2id['<PAD>'])
optimizer = optim.AdamW(model.parameters(), lr=learning_rate)
# Training loop
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
for epoch in range(num_epochs):
for batch in dataloader:
batch = batch.to(device)
# Prepare inputs and targets
inputs = batch[:, :-1]
targets = batch[:, 1:]
# Forward pass
outputs = model(inputs)
# Compute loss
loss = criterion(outputs.reshape(-1, vocab_size), targets.reshape(-1))
# Backward pass and optimization
optimizer.zero_grad()
loss.backward()
optimizer.step()
print(f"Epoch: {epoch+1}, Loss: {loss.item()}")
print("Training finished!")
Langkah 5: Inferensi
Inferensi melibatkan penggunaan model terlatih untuk menghasilkan teks. Proses ini melibatkan pemberian makan input ke model dan menghasilkan token secara iteratif hingga kondisi penghentian terpenuhi.
Generasi Teks
Generasi teks dilakukan secara iteratif. Dimulai dengan token awal (misalnya, <BOS>
), model memprediksi token berikutnya, yang ditambahkan ke urutan yang dihasilkan. Proses ini diulangi hingga token <EOS>
dihasilkan atau panjang maksimum tercapai.
Sampling Strategies (Greedy, Temperature Sampling)
Sampling strategies memengaruhi keberagaman dan kualitas teks yang dihasilkan.
- Greedy Sampling: Pilih token dengan probabilitas tertinggi di setiap langkah. Ini menghasilkan teks yang deterministik dan sering kali berulang.
- Temperature Sampling: Bagi logit dari prediksi model dengan suhu (temperature) dan kemudian menerapkan softmax. Temperatur yang lebih tinggi meningkatkan keacakan, sedangkan temperatur yang lebih rendah membuat prediksi lebih deterministik.
Implementasi Inferensi
def generate_text(model, tokenizer, start_sequence, max_length=50, temperature=1.0):
model.eval()
device = next(model.parameters()).device
input_ids = tokenizer.encode(start_sequence).ids
input_ids = torch.tensor(input_ids).unsqueeze(0).to(device)
generated_sequence = input_ids.tolist()[0]
with torch.no_grad():
for _ in range(max_length):
outputs = model(input_ids)
# Apply temperature
logits = outputs[:, -1, :] / temperature
probs = F.softmax(logits, dim=-1)
# Sample from the distribution
next_token = torch.multinomial(probs, num_samples=1).item()
generated_sequence.append(next_token)
input_ids = torch.tensor([generated_sequence]).to(device)
if next_token == tokenizer.token_to_id('<EOS>'):
break
# Decode the generated sequence
decoded_sequence = tokenizer.decode(generated_sequence)
return decoded_sequence
# Example Usage:
start_sequence = "<BOS> This is"
generated_text = generate_text(model, tokenizer, start_sequence)
print(f"Generated text: {generated_text}")
Langkah 6: Evaluasi dan Tuning
Evaluasi dan tuning sangat penting untuk meningkatkan kinerja model GPT.
Perplexity
Perplexity mengukur seberapa baik model memprediksi urutan. Semakin rendah perplexity, semakin baik modelnya.
Rumus Perplexity: Perplexity = exp(CrossEntropyLoss)
Evaluasi Kualitatif
Evaluasi kualitatif melibatkan pemeriksaan teks yang dihasilkan oleh model dan mengevaluasi koherensi, relevansi, dan kualitas keseluruhannya.
Hyperparameter Tuning
Hyperparameter tuning melibatkan eksperimen dengan nilai yang berbeda dari hyperparameter seperti learning rate, ukuran batch, jumlah lapisan, dan ukuran dimensi embedding untuk menemukan konfigurasi optimal untuk kumpulan data dan tugas Anda.
Kesimpulan
Dalam postingan blog ini, kita telah membahas langkah-langkah yang terlibat dalam melatih model GPT dari awal menggunakan PyTorch. Kami telah membahas persiapan data, tokenisasi, arsitektur Transformer, pelatihan model, inferensi, dan evaluasi. Dengan mengikuti langkah-langkah ini, Anda dapat membangun model GPT Anda sendiri dan menyesuaikannya dengan kebutuhan khusus Anda.
Langkah Selanjutnya
- Eksperimen dengan arsitektur yang berbeda: Cobalah arsitektur Transformer yang berbeda seperti Transformer-XL atau Reformer.
- Gunakan teknik regulasi yang berbeda: Eksperimen dengan teknik regulasi seperti dropout, weight decay, dan label smoothing.
- Fine-tune pada tugas khusus: Fine-tune model GPT terlatih Anda pada tugas NLP tertentu seperti klasifikasi teks atau terjemahan bahasa.
- Perluas vocabulary: Kembangkan vocabulary Anda untuk mencakup token tambahan yang relevan dengan kumpulan data Anda.
- Eksplorasi teknik optimalisasi: Terapkan teknik optimalisasi seperti gradient accumulation dan mixed-precision training.
Semoga beruntung dengan petualangan pelatihan model GPT Anda!
“`