Lewati ke konten utama

Model LSTM: Panduan Lengkap Long Short-Term Memory Networks

Kuasai cara kerja internal jaringan LSTM, fondasi bagi LLM modern. Jelajahi mekanisme gerbang, gradien, dan bangun pengklasifikasi sentimen dengan PyTorch.
· 15 mnt Baca

Jelajahi bersama AI

Jaringan saraf berulang standar kesulitan dengan memori. Saat urutans bertambah melampaui 20-30 timestep, gradien menyusut menuju nol atau meledak selama backpropagation, dan jaringan kehilangan kemampuan untuk menghubungkan bagian-bagian input yang berjauhan. Hochreiter dan Schmidhuber mengatasi masalah ini dalam makalah tahun 1997 dengan memperkenalkan jaringan Long Short-Term Memory.

Dalam panduan ini, saya akan menelusuri internal LSTM sebelum beralih ke implementasi praktis di Python. Bagian akhir membandingkan LSTM dengan Transformer agar Anda dapat memilih arsitektur yang tepat untuk kasus penggunaan Anda.

Saya sangat merekomendasikan kursus Introduction to Deep Learning in PyTorch kami untuk mendapatkan pengalaman langsung menggunakan PyTorch untuk melatih jaringan saraf, yang menjadi dasar model LSTM.

Apa Itu Model LSTM?

LSTM adalah singkatan dari Long Short-Term Memory, sejenis jaringan saraf berulang (RNN) yang dirancang untuk menangani urutan di mana konteks dari jauh sebelumnya pada input masih penting.

Gagasan inti di balik LSTM adalah mekanisme gerbang yang mengatur aliran informasi melalui jaringan. Alih-alih membiarkan semua sinyal lewat secara setara, gerbang mempelajari kapan harus mempertahankan konteks dan kapan harus melepaskannya. 

Memori selektif ini memungkinkan LSTM melacak dependensi lintas ratusan timestep, yang terbukti berguna untuk pemodelan bahasa, pengenalan ucapan, dan peramalan deret waktu. Untuk memahami mengapa arsitektur ini ada, ada baiknya melihat apa yang rusak pada RNN standar.

Masalah vanishing gradient pada RNN

Jaringan berulang menyerahkan informasi dari satu timestep ke timestep berikutnya melalui hidden state. Setiap langkah mengalikan state ini dengan bobot yang dipelajari, memperbaruinya, lalu meneruskannya. Pelatihan membutuhkan pengiriman sinyal kesalahan mundur melalui semua langkah tersebut, dan setiap lintasan mundur sedikit menyusutkan sinyal. Setelah cukup banyak langkah, tidak ada yang tersisa.

Ini seperti memfotokopi dari fotokopi. Lakukan beberapa kali, hasilnya masih bagus. Lakukan lima puluh kali, Anda nyaris tak bisa membaca teksnya. RNN mencapai batas ini di sekitar 20-30 timestep, itulah mengapa mereka kesulitan dengan urutan yang lebih panjang.

Bagaimana LSTM menyelesaikan dependensi jangka panjang

LSTM menambahkan jalur memori terpisah, disebut cell state, yang berjalan paralel dengan hidden state standar. Perbedaannya bersifat struktural: informasi dalam cell state mengalir melalui penjumlahan alih-alih perkalian berulang, sehingga tidak terdegradasi dengan cara yang sama.

Untuk mengontrol apa yang masuk dan keluar dari memori ini, LSTM menggunakan gerbang. Setiap gerbang adalah jaringan saraf kecil yang belajar menghasilkan nilai antara 0 dan 1, bertindak sebagai filter. Nilai mendekati 0 memblokir informasi; nilai mendekati 1 membiarkannya lewat. Jaringan mempelajari selama pelatihan pola mana yang layak diingat lintas urutan panjang dan mana yang dapat dibuang.

Mekanisme gerbang inilah yang membedakan LSTM dari RNN biasa. Alih-alih memaksa semua informasi melalui jalur yang sama, LSTM dapat secara selektif mempertahankan konteks lintas ratusan timestep. Bagian berikut menguraikan secara tepat bagaimana gerbang ini bekerja dan bagaimana gabungannya memperbarui cell state.

Kasus penggunaan model LSTM

LSTM muncul di mana pun data berurutan penting. Beberapa contoh di luar aplikasi NLP dan peramalan yang umum:

  • Bank menjalankan urutan transaksi melalui LSTM untuk menangkap pola penipuan yang luput dari sistem berbasis aturan
  • Rumah sakit memantau aliran ECG dengan LSTM dua arah, mendeteksi anomali jantung lebih cepat daripada peringatan ambang
  • Platform smart city memprediksi lalu lintas dan permintaan energi dari umpan sensor, menyesuaikan sumber daya sebelum lonjakan terjadi
  • Pabrik melacak getaran peralatan dari waktu ke waktu untuk menjadwalkan perawatan sebelum terjadi kerusakan

Arsitektur LSTM: Gerbang dan Sel Memori

Bagian ini membahas internal LSTM secara detail. Jika Anda ingin langsung ke implementasi, lewati ke bagian berikutnya. Rincian arsitektur di sini tidak wajib untuk menggunakan LSTM, tetapi membantu saat debug atau penyetelan.

Untuk memahami bagaimana LSTM bekerja, kita akan menelusuri contoh konkret: memproses urutan "The clouds are dark..." untuk memprediksi kata berikutnya. Setiap komponen lebih masuk akal saat Anda melihat apa yang dilakukannya pada data nyata.

Cell state dan hidden state dalam LSTM

Sebuah LSTM mempertahankan dua vektor terpisah yang mengalir melalui jaringan: cell state dan hidden state.

Cell state bertindak sebagai penyimpanan jangka panjang. Ia membawa informasi lintas timestep dengan modifikasi terkontrol di setiap langkah. Saat memproses urutan kita, cell state mungkin menyimpan "muncul kata benda terkait cuaca" setelah melihat "clouds" dan mempertahankan konteks itu melalui "are", hingga "dark" memperkuat polanya.

Hidden state adalah keluaran sel pada setiap timestep. Ini masuk ke prediksi dan ke perhitungan timestep berikutnya. Setelah memproses "dark," hidden state mengenkode sesuatu seperti "adjektiva yang menggambarkan kondisi cuaca" yang dapat digunakan oleh lapisan prediksi.

LSTM Cell

Mengapa dua jalur terpisah? Satu state harus sekaligus menampung konteks jangka panjang dan menghasilkan keluaran yang berguna. Tujuan ini bertentangan: memori yang stabil memerlukan perubahan minimal, sementara keluaran yang berguna memerlukan transformasi. Memisahkannya memungkinkan setiap jalur mengoptimalkan tujuannya.

Pada setiap timestep, sel menerima tiga input dan menghasilkan dua output:

Input:

  • Cell state sebelumnya (C_{t-1}): apa yang diingat jaringan sejauh ini

  • Hidden state sebelumnya (h_{t-1}): keluaran timestep terakhir

  • Input saat ini (x_t): token baru, seperti "clouds" atau "dark"

Output:

  • Cell state yang diperbarui (C_t): memori setelah memproses token ini

  • Hidden state baru (h_t): keluaran timestep ini

Bagaimana gerbang LSTM menghitung

Empat komponen mengontrol apa yang terjadi di dalam sel. Tiga di antaranya adalah gerbang yang menghasilkan nilai antara 0 dan 1, bertindak sebagai filter. Yang keempat adalah kandidat, yang mengusulkan konten baru.

Saat "are" datang setelah "clouds," setiap komponen memiliki tugas:

  • Forget gate (f_t): Haruskah kita mempertahankan konteks "clouds"? Kata benda cuaca yang diikuti verba penghubung biasanya penting, jadi forget gate menghasilkan nilai mendekati 1 untuk mempertahankan sebagian besar cell state.

  • Input gate (i_t): Seberapa banyak "are" harus berkontribusi pada memori? Verba penghubung membawa sinyal prediktif yang lebih rendah, jadi input gate menghasilkan nilai yang lebih rendah.

  • Kandidat (C̃_t): Informasi baru apa yang bisa ditambahkan oleh "are"? Kandidat mengusulkan konten, tetapi input gate menskalakannya turun.

  • Output gate (o_t): Apa yang harus ditampilkan hidden state? Output gate memfilter cell state untuk menghasilkan h_t.

How LSTM gates compute

Setiap komponen menghitung keluarannya dengan cara yang sama:

  1. Konkatenasi h_{t-1} dan x_t menjadi satu vektor

  2. Kalikan dengan matriks bobot yang dipelajari

  3. Tambahkan bias

  4. Terapkan fungsi aktivasi

Gerbang menggunakan aktivasi sigmoid, yang meremas nilai ke rentang 0-1. Kandidat menggunakan tanh, menghasilkan nilai antara -1 dan 1, sehingga dapat mengusulkan peningkatan maupun penurunan pada cell state.

Persamaan

Forget gate (sigmoid → 0 hingga 1):

Input gate (sigmoid → 0 hingga 1):

Output gate (sigmoid → 0 hingga 1):

Kandidat (tanh → -1 hingga +1):

Setiap komponen memiliki matriks bobotnya sendiri (W_f, W_i, W_c, W_o), sehingga mereka belajar merespons pola yang berbeda dalam input.

Bagaimana pemfilteran bekerja

Keluaran gerbang mengalikan vektor posisi demi posisi (perkalian elemen, ditulis sebagai ⊙). Setiap dimensi difilter secara independen.

Jika gerbang menghasilkan 0,9 untuk posisi 1, dimensi itu mempertahankan 90% nilainya. Jika menghasilkan 0,0 untuk posisi 4, dimensi itu dihapus. Saat memproses "dark" setelah "clouds are," forget gate mungkin mengeluarkan nilai tinggi untuk dimensi yang menyimpan konteks kata benda/cuaca dan nilai lebih rendah untuk dimensi yang menampung konteks awal yang tidak relevan.

Aliran informasi antara gerbang lupa, tambah, dan keluaran

Komponen bergabung dalam tiga langkah untuk memperbarui cell state dan menghasilkan hidden state.

Langkah 1: Lupa

Forget gate memfilter cell state sebelumnya:

Saat memproses "dark," forget gate mempertahankan konteks cuaca dari "clouds" sambil membersihkan dimensi yang melacak artikel "The."

Langkah 2: Tambah

Input gate menskalakan usulan kandidat, lalu menambahkannya ke cell state yang telah difilter:

Kandidat mungkin mengusulkan "adjektiva yang memodifikasi kata benda cuaca," dan input gate memutuskan seberapa kuat menuliskannya ke memori. Pembaruan aditif ini (bukan perkalian) yang membantu aliran gradien selama pelatihan.

Langkah 3: Keluaran

Cell state melewati tanh, lalu output gate memfilter apa yang menjadi hidden state:

Tanh menjaga nilai tetap dibatasi. Output gate memilih aspek mana dari memori yang terakumulasi yang harus terlihat untuk prediksi pada timestep ini.

Urutan lengkap:

Berikut jejak yang disederhanakan dengan cell state berdimensi 4:

Langkah

Input

Cell state (disederhanakan)

Apa yang terjadi

1

"The"

[0.1, 0.0, 0.2, 0.0]

Penyimpanan minimal; artikel membawa sedikit sinyal prediktif

2

"clouds"

[0.1, 0.7, 0.2, 0.3]

Input gate terbuka untuk dimensi 2,4; menyimpan pola kata benda

3

"are"

[0.1, 0.6, 0.2, 0.3]

Forget gate ≈ 0.9 mempertahankan konteks; verba penghubung menambah sedikit

4

"dark"

[0.1, 0.8, 0.5, 0.6]

Adjektiva memperkuat sinyal pada dimensi 2,3,4

Pada timestep ke-4, dimensi 2-4 telah mengakumulasi pola. Hidden state h_4 masuk ke lapisan prediksi (bukan bagian dari LSTM), yang menghasilkan probabilitas kosakata. Pola yang terakumulasi itu membantu model memihak kata-kata seperti "rain" atau "sky."

Jaringan tidak memahami cuaca. Ia mempelajari korelasi statistik: urutan input tertentu cenderung diikuti keluaran tertentu.

Aliran gradien selama pelatihan

Struktur aditif cell state menyediakan jalur gradien yang lebih langsung daripada RNN standar. Jika Anda belum familiar dengan bagaimana gradien merambat melalui jaringan saraf, tutorial backpropagation membahas dasar-dasarnya.

Pada RNN standar, gradien mengalir melalui matriks bobot di setiap langkah. Perkalian berulang mengakumulasi: setelah 100 langkah dengan bobot rata-rata 0,9, gradien menyusut menjadi 0.9^100 ≈ 0.00003.

LSTM menawarkan jalur alternatif di sepanjang cell state. Turunan C_t terhadap C_{t-1}:

∂C_t/∂C_{t-1} = f_t

Ini adalah nilai forget gate, bukan matriks bobot. Saat forget gate tetap mendekati 1, gradien melewati dengan lebih sedikit peluruhan. Jika gerbang rata-rata 0,95, setelah 100 langkah: 0.95^100 ≈ 0.006. Masih berkurang, tapi masi h dapat digunakan.

Aspek

RNN

LSTM

Jalur gradien

Hanya matriks bobot

Jalur tambahan di sepanjang cell state

Gradien 100 langkah

~10^-5 (hilang)

~10^-2 hingga 10^-1 (dapat digunakan)

Memori praktis

~10-20 timestep

~50-200 timestep

Catatan praktis: Bias forget gate sering diinisialisasi ke 1,0 atau lebih tinggi, mendorong keluaran awal ke arah "ingat semuanya." Ini membantu jaringan mempelajari pola jangka panjang di awal pelatihan.

LSTM mengurangi masalah vanishing gradient alih-alih menghilangkannya. Peningkatan berasal dari pembaruan cell state yang aditif, yang menyediakan jalur cepat gradien di samping jalur standar.

Cara Mengimplementasikan Model LSTM di Python

Bagian sebelumnya membahas apa yang terjadi di dalam sel LSTM: gerbang memfilter informasi, cell state mempertahankan konteks, dan hidden state menghasilkan keluaran. Sekarang kita akan melihat bagaimana nn.LSTM milik PyTorch membungkus semuanya ke dalam satu modul dan menggunakannya untuk mengklasifikasikan ulasan film sebagai positif atau negatif.

Tips menjalankan kode: Melatih selama 50 epoch di CPU memakan waktu lama. Gunakan Google Colab atau Kaggle Notebooks dengan akselerasi GPU untuk hasil lebih cepat. Skrip lengkap tersedia sebagai GitHub Gist.

Membangun Pengklasifikasi LSTM dengan PyTorch

Klasifikasi teks dengan LSTM mengikuti pola: ubah kata menjadi vektor, biarkan LSTM memprosesnya secara berurutan, lalu gunakan keluaran akhir untuk prediksi.

Menyematkan kata ke dalam vektor

Jaringan saraf beroperasi pada angka kontinu, tetapi kata adalah simbol diskret. Kita tidak bisa memasukkan "terrible" langsung ke perkalian matriks. Solusinya adalah lapisan embedding: tabel lokup di mana setiap kata dipetakan ke vektor yang dapat dipelajari.

self.embedding = nn.Embedding(vocab_size, embedding_dim, padding_idx=padding_idx)

Jika Anda memilih embedding_dim=32, setiap kata menjadi vektor berdimensi 32. Di awal pelatihan, vektor-vektor ini acak. Saat model belajar, kata-kata dengan sentimen serupa bergeser lebih dekat satu sama lain di ruang vektor ini. "Terrible," "awful," dan "boring" mungkin berakhir berdekatan, sementara "excellent" dan "amazing" mengelompok di tempat lain.

Argumen padding_idx memberi tahu lapisan untuk menjaga token padding sebagai vektor nol, sehingga tidak memengaruhi model.

Mendefinisikan arsitektur LSTM

self.lstm = nn.LSTM(
	input_size=embedding_dim,
	hidden_size=hidden_size,
	num_layers=num_layers,
	batch_first=True,
	dropout=dropout if num_layers > 1 else 0,
	bidirectional=bidirectional,
)

Satu baris ini membuat seluruh mesin gerbang dari bagian sebelumnya. hidden_size menentukan seberapa banyak informasi yang dapat dibawa oleh cell state dan hidden state. Nilai yang lebih besar memungkinkan model melacak lebih banyak pola tetapi memerlukan komputasi lebih.

Mengatur batch_first=True berarti input berbentuk (batch, sequence_length, embedding_dim). LSTM memproses setiap posisi dari kiri ke kanan, memperbarui state internalnya di setiap langkah.

Memproses urutan

Setelah memproses "This movie was absolutely terrible," LSTM telah melihat setiap kata. Hidden state akhirnya telah mengakumulasi apa pun yang dipelajari model untuk diekstrak dari urutan itu. Untuk sentimen, ini mungkin mengenkode sesuatu seperti "adjektiva negatif kuat di akhir, menegasikan konten netral sebelumnya."

def forward(self, x):
	embedded = self.embedding(x)
	lstm_out, (h_n, c_n) = self.lstm(embedded)
	h_final = h_n[-1]
	return self.fc(h_final)

LSTM mengembalikan keluaran per timestep (lstm_out) dan state akhir (h_n, c_n). Untuk klasifikasi, kita hanya membutuhkan h_n[-1], hidden state setelah token terakhir. Lapisan linear kemudian memetakan ini ke skor kelas.

Menelusuri sebuah ulasan melalui model

Saat "This movie was terrible" masuk ke pengklasifikasi:

  1. Setiap kata mendapatkan embedding-nya: empat vektor berukuran (32,)

  2. LSTM memprosesnya secara berurutan. "This" menginisialisasi beberapa cell state. "Movie" memperbaruinya sedikit. "Was" netral dan mengubah sedikit. "Terrible" memicu forget gate untuk membersihkan konteks yang tidak relevan dan input gate untuk menulis sinyal negatif yang kuat.

  3. Hidden state akhir menangkap pola yang terakumulasi.

  4. Lapisan linear memetakan ini ke dua skor: satu untuk positif, satu untuk negatif

Model tidak memahami bahasa. Ia mempelajari pola statistik: urutan kata tertentu berkorelasi dengan label tertentu.

Kelas model lengkap

Di bawah ini, Anda dapat melihat seluruh kelas LSTMClassifier yang baru saja kita buat langkah demi langkah:

import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
from datasets import load_dataset
from collections import Counter
import numpy as np
 
torch.manual_seed(42)
np.random.seed(42)
 
 
class LSTMClassifier(nn.Module):
    def __init__(
        Self,
        Vocab_size,
        Embedding_dim,
        Hidden_size,
        Output_size,
        num_layers=1,
        dropout=0.0,
        bidirectional=False,
        padding_idx=0,
    ):
        super().__init__()
        self.hidden_size = hidden_size
    	self.num_layers = num_layers
    	self.bidirectional = bidirectional
 
    	self.embedding = nn.Embedding(
	    vocab_size, embedding_dim, padding_idx=padding_idx
        )
    	self.lstm = nn.LSTM(
            input_size=embedding_dim,
            hidden_size=hidden_size,
            num_layers=num_layers,
            batch_first=True,
            dropout=dropout if num_layers > 1 else 0,
            bidirectional=bidirectional,
    	)	
 
    	fc_input_size = hidden_size * 2 if bidirectional else hidden_size
    	self.fc = nn.Linear(fc_input_size, output_size)
 
    def forward(self, x):
    	embedded = self.embedding(x)
    	lstm_out, (h_n, c_n) = self.lstm(embedded)
 
    	if self.bidirectional:
            h_final = torch.cat([h_n[-2], h_n[-1]], dim=1)
    	else:
            h_final = h_n[-1]
 
    	return self.fc(h_final)

Opsi bidirectional menjalankan dua LSTM: satu dari kiri ke kanan, satu dari kanan ke kiri. Hidden state akhir keduanya digabungkan, memberi model konteks dari kedua arah. Ini membantu saat makna bergantung pada apa yang datang sesudahnya, seperti "not bad," di mana "not" hanya masuk akal jika ada "bad."

Menyiapkan teks untuk model

LSTM membutuhkan urutan bilangan bulat, bukan string. Pra-pemrosesan mengonversi teks mentah melalui tiga langkah: 

  1. Membangun kosakata yang memetakan kata ke indeks
  2. Mengonversi setiap ulasan menjadi urutan indeks
  3. Melakukan padding urutan ke panjang seragam
def build_vocab(texts, max_vocab_size=10000):
	counter = Counter()
	for text in texts:
    		counter.update(text.lower().split())
 
	vocab = {"<pad>": 0, "<unk>": 1}
	for word, _ in counter.most_common(max_vocab_size - 2):
    		vocab[word] = len(vocab)
	return vocab

Kami mencadangkan indeks 0 untuk padding dan indeks 1 untuk kata yang tidak dikenal. Membatasi ukuran kosakata menyaring kata-kata langka yang tidak memiliki cukup contoh untuk mempelajari embedding yang bermakna.

def tokenize_and_pad(texts, vocab, max_length=200):
	sequences = []
	for text in texts:
    		tokens = text.lower().split()
    		ids = [vocab.get(t, vocab["<unk>"]) for t in tokens[:max_length]]
    		ids = ids + [vocab["<pad>"]] * (max_length - len(ids))
    		sequences.append(ids)
	return torch.tensor(sequences, dtype=torch.long)

Memotong hingga max_length membatasi seberapa jauh ke belakang LSTM harus mengingat. Urutan yang lebih panjang mempertahankan lebih banyak konteks tetapi meningkatkan biaya komputasi dan memori. Untuk ulasan film, 100-200 token biasanya menangkap sinyal sentimen kunci.

Padding memastikan setiap urutan memiliki panjang yang sama, memungkinkan PyTorch mengelompokkannya menjadi satu tensor. Token padding menjadi vektor nol setelah embedding dan berkontribusi sedikit pada state akhir.

def load_imdb_data(max_samples=10000, max_length=200, max_vocab=10000):
	dataset = load_dataset("imdb")
 
	train_data = dataset["train"].shuffle(seed=42)
	test_data = dataset["test"].shuffle(seed=42)
 
	train_texts = train_data["text"][:max_samples]
	train_labels = train_data["label"][:max_samples]
	test_texts = test_data["text"][: max_samples // 4]
	test_labels = test_data["label"][: max_samples // 4]
 
	vocab = build_vocab(train_texts, max_vocab)
 
	X_train = tokenize_and_pad(train_texts, vocab, max_length)
	X_test = tokenize_and_pad(test_texts, vocab, max_length)
	y_train = torch.tensor(train_labels, dtype=torch.long)
	y_test = torch.tensor(test_labels, dtype=torch.long)
 
	return X_train, y_train, X_test, y_test, vocab

Membangun kosakata hanya dari data pelatihan mencegah kebocoran informasi. Jika kata langka hanya muncul di ulasan uji, model harus memperlakukannya sebagai tidak dikenal alih-alih telah mempelajari embedding-nya selama pelatihan.

Pelatihan LSTM dan hiperparameter

Loop pelatihan memperbarui bobot model berdasarkan seberapa jauh prediksinya dari nilai yang benar. Untuk LSTM, satu detail lebih penting dari biasanya: pemotongan gradien.

def train_model(model, train_loader, test_loader, n_epochs=5, lr=0.001):
	criterion = nn.CrossEntropyLoss()
	optimizer = torch.optim.Adam(model.parameters(), lr=lr)
 
	for epoch in range(n_epochs):
    		model.train()
    		total_loss, correct, total = 0, 0, 0
 
    		for batch_texts, batch_labels in train_loader:
        		optimizer.zero_grad()
        		outputs = model(batch_texts)
        		loss = criterion(outputs, batch_labels)
        		loss.backward()
            		torch.nn.utils.clip_grad_norm_(
	model.parameters(), 
	max_norm=5.0)
        		optimizer.step()
 
        		total_loss += loss.item()
        		_, predicted = outputs.max(1)
        		correct += (predicted == batch_labels).sum().item()
        		total += batch_labels.size(0)

Mengapa pemotongan gradien penting untuk LSTM

Sebelumnya, kami menjelaskan bagaimana LSTM mengurangi vanishing gradient melalui pembaruan aditif pada cell state. Namun exploding gradient tetap berisiko. Saat gradien menjadi terlalu besar, pembaruan bobot melampaui sasaran dan membuat pelatihan tidak stabil.

clip_grad_norm_ membatasi besaran total gradien. Jika gradien akan melebihi max_norm=5.0, gradien diskalakan turun secara proporsional. Ini menjaga pembaruan tetap stabil tanpa menghilangkan sinyal gradien sepenuhnya.

    	model.eval()
    	test_correct, test_total = 0, 0
    	with torch.no_grad():
        	for batch_texts, batch_labels in test_loader:
            		outputs = model(batch_texts)
            		_, predicted = outputs.max(1)
            		test_correct += (predicted == batch_labels).sum().item()
            		test_total += batch_labels.size(0)
 
    	test_acc = test_correct / test_total
    	print(f"Epoch {epoch+1}: Loss={total_loss/len(train_loader):.4f}, "
          	f"Train={correct/total:.3f}, Test={test_acc:.3f}")
 
    return test_acc

model.eval() menonaktifkan dropout saat evaluasi. torch.no_grad() melewati perhitungan gradien karena kita tidak memperbarui bobot, yang menghemat memori dan mempercepat inferensi.

Memilih hiperparameter

X_train, y_train, X_test, y_test, vocab = load_imdb_data(
	max_samples=10000, max_length=100, max_vocab=5000
)
 
train_loader = DataLoader(TensorDataset(X_train, y_train), batch_size=64, shuffle=True)
test_loader = DataLoader(TensorDataset(X_test, y_test), batch_size=64)
 
model = LSTMClassifier(
	vocab_size=len(vocab),
	embedding_dim=32,
	hidden_size=32,
	output_size=2,
	padding_idx=vocab["<pad>"],
)
 
final_acc = train_model(model, train_loader, test_loader, n_epochs=50, lr=0.001)

Nilai-nilai ini mencerminkan kompromi:

  • hidden_size=32: Cukup kecil untuk iterasi cepat. Model produksi sering menggunakan 128-512, tetapi seiring bertambahnya ukuran, keuntungan menurun dan pelatihan melambat.

  • embedding_dim=32: Menyamakan dengan hidden size lazim tetapi tidak wajib. Embedding yang lebih besar menangkap perbedaan kata lebih halus dengan biaya lebih banyak parameter.

  • max_length=100: Sebagian besar sinyal sentimen muncul di awal ulasan. Urutan yang lebih panjang membantu kasus yang bernuansa, tetapi melipatgandakan penggunaan memori untuk max_length yang dua kali lebih panjang.

  • batch_size=64: Batch yang lebih besar memberikan estimasi gradien yang lebih stabil. Batch lebih kecil dapat membantu keluar dari local minima. 32-128 adalah tipikal.

  • lr=0.001: Bawaan Adam bekerja untuk sebagian besar tugas LSTM. Jika loss berosilasi, coba 0,0001. Jika pembelajaran mandek, coba 0,01.

  • n_epochs=50: Cukup untuk model berkovergensi. Amati akurasi uji yang mendatar atau menurun sementara akurasi pelatihan terus naik, yang menandakan overfitting.

Apa yang dipelajari model

Menjalankan konfigurasi ini menghasilkan keluaran seperti:

...
Epoch 46: Loss=0.4126, Train=0.812, Test=0.789
Epoch 47: Loss=0.4054, Train=0.815, Test=0.791
Epoch 48: Loss=0.3998, Train=0.818, Test=0.788
Epoch 49: Loss=0.3921, Train=0.821, Test=0.793
Epoch 50: Loss=0.3867, Train=0.824, Test=0.795

Akurasi pelatihan mencapai sekitar 82% sementara akurasi uji menetap di sekitar 80%. Model mempelajari pola kata mana yang memprediksi sentimen. Adjektiva kuat di dekat akhir membawa bobot. Negasi seperti "not" membalik polaritas. Frasa tertentu ("waste of time," "highly recommend") menjadi sinyal kuat. Semua ini tidak diprogram; muncul dari korelasi dalam data pelatihan.

Dengan penyetelan minimal dan model kecil, akurasi 80% adalah wajar. Kesenjangan kecil antara akurasi pelatihan dan uji menunjukkan model melakukan generalisasi dengan baik tanpa overfitting parah. Menambahkan dropout, menggunakan embedding pra-latih seperti GloVe, atau melatih pada lebih banyak data akan mendorong akurasi ke kisaran akhir 80%.

Meningkatkan Performa Model LSTM

Arsitektur LSTM dasar dari bagian sebelumnya berfungsi, tetapi beberapa teknik dapat mendorong akurasi lebih tinggi tanpa beralih ke keluarga model lain. Pendekatan ini telah divalidasi di ribuan penerapan produksi dan makalah riset.

LSTM dua arah

LSTM standar memproses urutan dari kiri ke kanan, membangun konteks hanya dari token masa lalu. LSTM dua arah (BiLSTM) menjalankan dua lintasan paralel: satu maju, satu mundur. Representasi akhir pada setiap timestep menggabungkan apa yang datang sebelum dan sesudah.

self.lstm = nn.LSTM(
	input_size=embedding_dim,
	hidden_size=hidden_size,
	bidirectional=True,  # Run both directions
	batch_first=True,
)
# Output size doubles: hidden_size * 2

Untuk analisis sentimen, ini penting ketika negasi muncul setelah kata yang dimodifikasinya: "the movie was not bad" membutuhkan konteks maju untuk mengetahui "bad" ada, dan konteks mundur untuk mengetahui "not" mendahuluinya. 

BiLSTM secara konsisten mengungguli model searah pada named entity recognition (NER), klasifikasi sentimen, dan pengenalan ucapan, dengan peningkatan tipikal 5-15%.

Konsekuensinya: Anda membutuhkan urutan penuh pada waktu inferensi. Aplikasi streaming yang menerima token satu per satu tidak dapat menggunakan pemrosesan dua arah.

Menambahkan attention

Attention memungkinkan model memberi bobot timestep mana yang paling penting untuk sebuah prediksi. Alih-alih memampatkan seluruh urutan ke satu hidden state akhir, attention menghitung kombinasi berbobot dari semua hidden state.

class AttentionLayer(nn.Module):
	def __init__(self, hidden_size):
    		super().__init__()
    		self.attention = nn.Linear(hidden_size, 1)
	
	def forward(self, lstm_outputs):
    	# lstm_outputs: (batch, seq_len, hidden_size)
    		scores = self.attention(lstm_outputs)  # (batch, seq_len, 1)
    		weights = torch.softmax(scores, dim=1)
    		context = torch.sum(weights * lstm_outputs, dim=1)
    		return context

Dalam ulasan seperti "The acting was wooden, the plot made no sense, but the cinematography was stunning," attention belajar untuk fokus pada frasa yang membawa sentimen alih-alih memperlakukan semua kata sama. Model memberikan bobot lebih tinggi pada "wooden," "no sense," dan "stunning" sambil menurunkan bobot "the" dan "was."

Attention menambah beban komputasi minimal dan sering memberikan peningkatan akurasi 2-5% pada tugas klasifikasi. Untuk masalah sequence-to-sequence seperti penerjemahan, attention antara encoder dan decoder nyaris wajib untuk kinerja yang baik.

Satu lapisan LSTM menangkap pola pada satu tingkat abstraksi. Menumpuk beberapa lapisan memungkinkan jaringan mempelajari fitur hierarkis: lapisan pertama mungkin mendeteksi pola frasa lokal, sementara lapisan lebih tinggi menggabungkannya menjadi pemahaman tingkat dokumen.

self.lstm = nn.LSTM(
	input_size=embedding_dim,
	hidden_size=hidden_size,
	num_layers=3,  # Stack three layers
	dropout=0.3,   # Dropout between layers (not within)
	batch_first=True,
)

Panduan untuk kedalaman:

  •  1 lapisan: Tugas sederhana, dataset kecil, iterasi cepat
  • 2 lapisan: Sebagian besar aplikasi produksi
  • 3-4 lapisan: Dependensi temporal kompleks, dataset besar
  • Lebih dari 4: Hasil berkurang, pelatihan tidak stabil

Setiap lapisan tambahan kira-kira menggandakan waktu pelatihan. Mulailah dari yang dangkal dan tambahkan kedalaman hanya jika metrik validasi mendatar.

Teknik regularisasi

LSTM mudah overfit, terutama pada dataset kecil. Tiga teknik regularisasi yang bekerja baik bersama:

  • Dropout
  • Pemotongan gradien
  • Early stopping

Dropout

Dropout secara acak men-nol-kan neuron selama pelatihan, memaksa jaringan mendistribusikan pembelajaran melalui beberapa jalur. Terapkan dropout di antara lapisan LSTM, bukan di dalam koneksi rekuren (yang dapat mengganggu pembelajaran temporal).

# PyTorch applies dropout between stacked layers automatically
self.lstm = nn.LSTM(..., num_layers=2, dropout=0.3)
 
# For single-layer LSTMs, add dropout after
self.dropout = nn.Dropout(0.3)

Tingkat dropout 0,2-0,5 bekerja untuk sebagian besar tugas. Nilai lebih tinggi membantu saat overfitting parah tetapi memperlambat konvergensi.

Pemotongan gradien

Pemotongan gradien membatasi besaran gradien selama backpropagation. Meskipun LSTM mengurangi vanishing gradient, exploding gradient masih terjadi, terutama dengan urutan panjang atau jaringan yang dalam.

torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=5.0)

Nilai antara 1,0 dan 5,0 adalah tipikal. Jika loss pelatihan melonjak atau menghasilkan nilai NaN, turunkan ambangnya.

Early stopping

Early stopping memantau loss validasi dan menghentikan pelatihan saat tidak lagi membaik. Ini mencegah model menghafal data pelatihan dengan mengorbankan generalisasi.

patience = 5
best_val_loss = float('inf')
epochs_without_improvement = 0
 
for epoch in range(max_epochs):
	train_loss = train_one_epoch(model, train_loader)
	val_loss = evaluate(model, val_loader)
	
	if val_loss < best_val_loss:
    		best_val_loss = val_loss
    		epochs_without_improvement = 0
    		torch.save(model.state_dict(), 'best_model.pt')
	else:
    		epochs_without_improvement += 1
	if epochs_without_improvement >= patience:
        	break

Untuk panduan lengkap tentang loop pelatihan, metrik evaluasi, dan debug masalah umum pada LSTM, lihat panduan NLP dengan PyTorch.

Embedding pra-latih

Inisialisasi embedding acak berfungsi, tetapi embedding pra-latih seperti GloVe atau Word2Vec memberi model awalan. Embedding ini mengenkode hubungan semantik yang dipelajari dari miliaran kata: "king" dan "queen" berakhir berdekatan, begitu juga "terrible" dan "awful."

import numpy as np
 
def load_glove_embeddings(glove_path, vocab, embedding_dim):
	embeddings = np.random.randn(len(vocab), embedding_dim) * 0.01
	
	with open(glove_path, 'r') as f:
    	for line in f:
        	parts = line.split()
        	word = parts[0]
        	if word in vocab:
            		embeddings[vocab[word]] = np.array(parts[1:], dtype=np.float32)
	
	return torch.tensor(embeddings, dtype=torch.float32)
 
# Load and freeze or fine-tune
pretrained = load_glove_embeddings('glove.6B.100d.txt', vocab, 100)
model.embedding.weight.data.copy_(pretrained)
model.embedding.weight.requires_grad = True  # Fine-tune during training

Embedding pra-latih sangat membantu saat data pelatihan terbatas. Dengan jutaan contoh, embedding yang diinisialisasi secara acak pada akhirnya menyusul. Untuk sebagian besar aplikasi praktis dengan 10k-100k sampel, harapkan peningkatan akurasi 3-8% dari inisialisasi pra-latih.

Menggabungkan teknik

Pendekatan ini dapat ditumpuk. Pengklasifikasi teks tingkat produksi mungkin menggunakan:

  • BiLSTM untuk konteks dua arah
  • 2 lapisan bertumpuk untuk fitur hierarkis
  • Attention pooling alih-alih hidden state akhir
  • Embedding GloVe yang disetel-halus selama pelatihan
  • Dropout 0,3 di antara lapisan
  • Pemotongan gradien pada 5,0
  • Early stopping dengan patience 5 epoch

Setiap tambahan meningkatkan kompleksitas dan waktu pelatihan. Mulailah dengan LSTM satu lapisan sebagai baseline, ukur performa, lalu tambahkan teknik satu per satu untuk melihat mana yang memberi peningkatan pada tugas spesifik Anda.

LSTM vs Transformer: Memilih Arsitektur yang Tepat

LSTM mendominasi pemodelan urutan dari akhir 1990-an hingga pertengahan 2010-an. Makalah 2017 "Attention Is All You Need" mengubah itu. Transformer kini menggerakkan sebagian besar model bahasa besar, tetapi LSTM belum hilang. Mereka mengisi ceruk yang berbeda.

Keterbatasan LSTM

Bagian sebelumnya membahas bagaimana LSTM memproses urutan satu per satu token dan mengapa mekanisme gerbang itu bekerja. Dalam skala besar, ketergantungan berurutan itu menjadi hambatan. GPU Anda mungkin memiliki ribuan inti, tetapi sebagian besar menganggur sementara rantai hidden state selesai. Input 500 token berarti 500 operasi berturut-turut.

Transformer tidak memiliki kendala ini. Self-attention menghitung hubungan antar semua posisi secara simultan, sehingga urutan 500 token yang sama diproses secara paralel. Pada perangkat keras modern dengan ukuran batch besar, ini diterjemahkan menjadi percepatan pelatihan 10-100x.

Batas panjang urutan juga penting. Kita melihat bagaimana LSTM mempertahankan gradien lebih baik daripada RNN biasa, tetapi "lebih baik" tidak berarti "sempurna." Performa masih turun di sekitar 200-500 token, tergantung tugasnya. 

Transformer menghitung attention antara setiap pasangan posisi secara langsung, sehingga token 1 memperhatikan token 1000 dengan kemudahan yang sama. Konsekuensinya adalah memori: matriks attention tumbuh secara kuadrat dengan panjang urutan.

Aktivasi gerbang juga membuat interpretasi sulit. Nilai berkisar dari 0 hingga 1 tanpa batas yang jelas. Bobot attention pada Transformer bukan penjelasan sempurna juga, tetapi memberikan sinyal yang lebih jelas tentang token input mana yang penting untuk setiap keluaran.

Kapan menggunakan LSTM vs transformer

Transformer masuk akal ketika:

  • Data pelatihan melebihi 100K contoh, dan Anda memiliki akses GPU
  • Tugas melibatkan dependensi yang membentang ratusan token
  • Kecepatan pelatihan penting untuk siklus iterasi
  • Model pra-latih seperti GPT dapat disetel-halus untuk kasus penggunaan Anda

LSTM masih cocok ketika:

  • Perangkat target memiliki batasan memori ketat (edge, embedded, mobile)
  • Data langka (di bawah 10K contoh), dan overfitting Transformer menjadi perhatian
  • Prediksi perlu dilakukan pada urutan parsial sebelum input penuh tiba
  • Anggaran latensi menuntut model yang ringan

Peramalan deret waktu berada di tengah-tengah. Tolok ukur terbaru menunjukkan kedua arsitektur berkinerja serupa pada data univariat, dengan pola yang konsisten. Transformer unggul saat banyak fitur berinteraksi secara kompleks, tetapi LSTM bertahan ketika data langka atau komputasi terbatas.

Desain hibrida memadukan kedua pendekatan. Beberapa arsitektur menumpuk lapisan LSTM dengan mekanisme attention. xLSTM, diterbitkan pada 2024, meninjau kembali desain asli dengan exponential gating dan memori matriks, menunjukkan hasil kompetitif dengan Transformer pada skala yang lebih kecil.

Aspek

LSTM

Transformer

Pemrosesan

Berurutan (token demi token)

Paralel (semua posisi sekaligus)

Kecepatan pelatihan

Lebih lambat (paralelisasi terbatas)

Lebih cepat di GPU

Dependensi jangka panjang

Menurun di atas 200-500 token

Menangani ribuan token

Skalabilitas memori

O(n) dengan panjang urutan

O(n²) untuk matriks attention

Performa data kecil

Baik (parameter lebih sedikit)

Rentan overfitting

Penerapan edge

Cocok (jejak memori lebih rendah)

Menantang (model besar)

Streaming/waktu nyata

Dukungan native

Membutuhkan urutan penuh

Ekosistem pra-latih

Terbatas

Luas (BERT, GPT, dll.)

Keputusan biasanya bergantung pada kendala. Komputasi dan data tak terbatas? Transformer menang pada sebagian besar tugas NLP. Mikro-kontroler dengan 10K contoh pelatihan? LSTM tetap praktis.

Kesimpulan

LSTM memecahkan masalah nyata saat diperkenalkan pada 1997. Mekanisme gerbang yang diperkenalkan oleh Hochreiter dan Schmidhuber memungkinkan jaringan akhirnya belajar dari urutan yang lebih panjang dari beberapa lusin token. Arsitektur itu menggerakkan penerjemahan mesin, pengenalan ucapan, dan peramalan deret waktu selama hampir dua dekade.

Transformer kini menangani sebagian besar tugas NLP skala besar, tetapi LSTM belum menjadi usang. Mereka masih masuk akal untuk penerapan edge, aplikasi streaming, dan situasi di mana data pelatihan terbatas.

Jika Anda memulai proyek pemodelan urutan baru, coba LSTM sederhana terlebih dahulu. Ukur performa, pahami mode kegagalannya, lalu putuskan apakah masalah tersebut benar-benar memerlukan Transformer. Konsep dari panduan ini (gerbang, cell state, aliran gradien) langsung berpindah ke pemahaman arsitektur yang lebih kompleks.

Untuk latihan langsung, saya merekomendasikan mengikuti kursus Recurrent Neural Networks, yang mengulas tambahan aplikasi LSTM dengan latihan terpandu.

LSTM FAQs

Apa perbedaan antara LSTM dan RNN biasa?

RNN biasa kesulitan dengan urutan panjang karena gradien menyusut selama backpropagation, sehingga kehilangan kemampuan untuk belajar dari konteks jauh. LSTM menambahkan cell state dengan mekanisme gerbang yang mengontrol aliran informasi, memungkinkan jaringan mempertahankan konteks yang relevan lintas ratusan timestep alih-alih hanya 20-30.

Kapan saya harus menggunakan LSTM alih-alih Transformer?

LSTM bekerja lebih baik saat data pelatihan terbatas (di bawah 10K contoh), perlu dijalankan pada perangkat dengan memori terbatas seperti ponsel atau sistem tertanam, memerlukan prediksi waktu nyata pada data streaming sebelum urutan penuh tiba, atau memiliki persyaratan latensi ketat yang menyingkirkan model yang lebih besar.

Apa arti hidden_size dalam PyTorch LSTM?

Parameter hidden_size menentukan dimensi vektor hidden state dan cell state. Nilai yang lebih besar memungkinkan model melacak lebih banyak pola, tetapi meningkatkan komputasi dan penggunaan memori. Nilai antara 32-512 lazim, dengan 128-256 tipikal untuk aplikasi teks produksi.

Bagaimana cara mencegah LSTM saya overfitting?

Gunakan dropout di antara lapisan LSTM (tingkat 0,2-0,5), terapkan pemotongan gradien untuk mencegah exploding gradient, terapkan early stopping untuk menghentikan pelatihan saat loss validasi berhenti membaik, dan pertimbangkan menggunakan embedding pra-latih seperti GloVe jika dataset Anda kecil.

Apa itu LSTM dua arah, dan kapan saya harus menggunakannya?

LSTM dua arah menjalankan dua lintasan pada urutan: satu maju dan satu mundur. Ini memberi setiap posisi konteks dari kedua arah, yang membantu saat makna bergantung pada token di masa depan (seperti "not bad" di mana "not" perlu melihat "bad"). Gunakan untuk tugas klasifikasi dan penandaan ketika Anda memiliki urutan lengkap saat inferensi, tetapi hindari untuk aplikasi streaming.

Bexruz (Bex) Tuychiev

Penulis

Bexruz (Bex) Tuychiev

Saya adalah kreator konten data science dengan pengalaman lebih dari 2 tahun dan salah satu dengan jumlah pengikut terbesar di Medium. Saya suka menulis artikel mendalam tentang AI dan ML dengan sedikit gaya sarkastik—karena harus ada sesuatu untuk membuatnya tidak terlalu membosankan. Saya telah menghasilkan lebih dari 130 artikel dan satu kursus di DataCamp, dengan satu lagi sedang dalam proses. Konten saya telah dilihat oleh lebih dari 5 juta pasang mata, dan 20 ribu di antaranya menjadi pengikut di Medium dan LinkedIn. 

Topik

Terkait

blogs

Tutorial Korelasi di R

blogs

40 Pertanyaan Wawancara DBMS Teratas di 2026

blogs

Spaghetti Plot dan Jalur Badai