Membuat Model Bahasa menggunakan Deep Learning
Model many-to-many LSTM adalah jenis arsitektur jaringan saraf tiruan (neural network) yang menggunakan lapisan LSTM (Long Short-Term Memory) untuk memproses serangkaian input dan menghasilkan serangkaian output yang sesuai. Model ini sering digunakan dalam tugas-tugas pemodelan urutan di mana input dan output berurutan, dan panjangnya tidak selalu sama.
Pada blog sebelumnya kita menggunakan many to one untuk mengklasifikasikan judul berita – Text Classification Model – Studi Kasus Penerapan Klasifikasi Judul Berita
Nah kita akan coba menggunakan LSTM berjenis many to many untuk membuat prediksi kalimat dari sebuah kumpulan kata-kata. Misalkan kita mempunyai kata berikut “Mazda merilis produk baru” nanti LSTM akan mengeluarkan kalimat/kata setelah nya.
Crawler Website / Corpus
Contents
Untuk mencari database yang paling gampang yaitu dengan melakukan crawler media masa online seperti kompas. Kalian bisa crawler web dengan alamat https://otomotif.kompas.com/. Sebelum crawler kalian bisa baca postingan berikut ini
- Text Classification Model Studi Kasus Penerapan Klasifikasi Judul Berita
- Fungsi Rekursif untuk Crawler Link Website dan Set Recursion Limit
kalau nggak mau capek-capek buat bot buat crawler, kalian bisa download database sqlite berikut ini crawler kompas.odb
Mengingat keterbatasan RAM komputer hanya mampu crawler 1.724 judul artikel saja. Saya sebut ini dengan corpus kompas/otomotif.
Corpus adalah
- Definisi: Corpus merujuk kepada kumpulan besar teks yang digunakan untuk analisis, pelatihan model, atau penelitian dalam bidang NLP atau linguistik komputasional.
- Isi: Corpus terdiri dari berbagai jenis teks, seperti artikel berita, buku, dokumen web, transkripsi percakapan, atau koleksi dokumen lainnya yang relevan dengan tujuan analisis atau penelitian tertentu.
- Ukuran: Ukuran corpus dapat bervariasi dari kecil hingga sangat besar, tergantung pada sumber data dan kebutuhan proyek tertentu. Beberapa corpus terkenal termasuk Corpus Brown, Penn Treebank, atau Wikipedia Corpus
Loading Text
Text database tersebut menggunakan sqlite, maka kita perlu membuat class untuk membaca file tersebut. Kalian bisa membaca Belajar Database Relational SQL Lite Bagian 3 – DB Browser Sqlite
import sqlite3 from sqlite3 import Error import pandas as pd class DB_Kompas(): def __init__(self,db_file): self.db_file = db_file self.conn = None self.sql_buat_tabel = """ CREATE TABLE IF NOT EXISTS kompas ( id integer PRIMARY KEY, url text, isi text ); """ self.buka() self.buat_tabel() def buka(self): try: self.conn = sqlite3.connect(self.db_file) return self.conn except Error as e: print(e) return self.conn def buat_tabel(self): try: c = self.conn.cursor() c.execute(self.sql_buat_tabel) except Error as e: print(e) def masukan_data(self,content): sql = ''' INSERT INTO kompas(url,isi) VALUES(?,?) ''' cur = self.conn.cursor() cur.execute(sql, (content)) self.conn.commit() def get(self): cur = self.conn.cursor() cur.execute("SELECT * FROM kompas") rows = cur.fetchall() data = list() for row in rows: # print(row) data.append(row) return pd.DataFrame(data)
Class tersebut berfungsi untuk membaca tabel saja kok, untuk membaca bisa kita panggil perintah berikut
db = DB_Kompas("kompas.odb") kompas = db.get()
Regex buat bersihkan simbol
Kita bisa melihat isi text dengan perintah dengan arti record ke 0 kolom 2
kompas.iloc[0][2]
hasilnya
'\n\nKOMPAS.com\xa0– Pada acara puncak Konferensi Tingkat Tinggi (KTT) G20 yang berlangsung di Nusa Dua, Bali, pada November 2022, seluruh kepala negara dan delegasi yang hadir terlihat memakai kendaraan listrik.\nPenggunaan mobil listrik sebagai kendaraan resmi dalam perhelatan KTT G20 merupakan bentuk komitmen Pemerintah Indonesia dalam mengurangi emisi karbon yang menjadi salah satu penyebab dari pemanasan global.\nAdapun dari sekian banyak kendaraan listrik yang ada, mobil berjenis\xa0battery electric vehicle\xa0(BEV) dari Toyota, yakni bZ4X, jadi salah satu yang paling mencuri perhatian.\nPasalnya, mobil listrik berbasis baterai pertama dari Toyota itu terlihat berkelas lantaran hadir dengan desain yang elegan dan futuristik. Teknologi dan fitur yang disematkan pun juga yang terbaik di kelasnya.\nOleh karena itu, tak heran jika mobil tersebut dibanderol dengan harga yang cukup tinggi, yakni mencapai Rp 1,190 miliar.\nBagi sebagian orang, harga itu mungkin terdengar anomali. Apalagi, para produsen otomotif roda empat lainnya kini tengah berlomba-lomba menawarkan produk mobil listrik dengan harga terjangkau.\nNamun, Toyota memiliki alasan kuat terkait banderol bZ4X yang mencapai angka miliaran rupiah.\nUntuk diketahui, bZ4X adalah terobosan baru dalam pasar mobil listrik karena hadir dengan filosofi "Beyond Zero" (bZ) yang mengusung konsep nol emisi dan memberikan pengalaman berkendara.\nSelain itu, produk bZ4X dapat dikatakan\xa0sebagai\xa0yang orisinal karena tidak hanya sekadar adaptasi dari model sebelumnya.\nHal tersebut lantaran bZ4X dibangun di atas platform baru yang didedikasikan khusus untuk kendaraan berjenis BEV, yakni Toyota New Global Architecture (TNGA).
atau bila kita menggunakan SQlite browser akan tampil text berikut yang lebih rapi
KOMPAS.com – Pada acara puncak Konferensi Tingkat Tinggi (KTT) G20 yang berlangsung di Nusa Dua, Bali, pada November 2022, seluruh kepala negara dan delegasi yang hadir terlihat memakai kendaraan listrik. Penggunaan mobil listrik sebagai kendaraan resmi dalam perhelatan KTT G20 merupakan bentuk komitmen Pemerintah Indonesia dalam mengurangi emisi karbon yang menjadi salah satu penyebab dari pemanasan global. Adapun dari sekian banyak kendaraan listrik yang ada, mobil berjenis battery electric vehicle (BEV) dari Toyota, yakni bZ4X, jadi salah satu yang paling mencuri perhatian. Pasalnya, mobil listrik berbasis baterai pertama dari Toyota itu terlihat berkelas lantaran hadir dengan desain yang elegan dan futuristik. Teknologi dan fitur yang disematkan pun juga yang terbaik di kelasnya. Oleh karena itu, tak heran jika mobil tersebut dibanderol dengan harga yang cukup tinggi, yakni mencapai Rp 1,190 miliar. Bagi sebagian orang, harga itu mungkin terdengar anomali. Apalagi, para produsen otomotif roda empat lainnya kini tengah berlomba-lomba menawarkan produk mobil listrik dengan harga terjangkau. Namun, Toyota memiliki alasan kuat terkait banderol bZ4X yang mencapai angka miliaran rupiah. Untuk diketahui, bZ4X adalah terobosan baru dalam pasar mobil listrik karena hadir dengan filosofi "Beyond Zero" (bZ) yang mengusung konsep nol emisi dan memberikan pengalaman berkendara. Selain itu, produk bZ4X dapat dikatakan sebagai yang orisinal karena tidak hanya sekadar adaptasi dari model sebelumnya. Hal tersebut lantaran bZ4X dibangun di atas platform baru yang didedikasikan khusus untuk kendaraan berjenis BEV, yakni Toyota New Global Architecture (TNGA).
Tokenizer
Tokenizer berguna untuk memecah kalimat menjadi kata-kata, mari kita lengkapi saja kodenya
import torch import torch.nn as nn import torch.optim as optim import math import torchtext import numpy as np from tqdm import tqdm berita = list() for t in kompas[2]: berita.append(t) # baca lebih lanjut https://softscients.com/2022/10/04/pytorch-studi-kasus-penerapan-nlp-pada-judul-berita/ tokenizer = torchtext.data.utils.get_tokenizer('basic_english') tokenize_data = lambda paragraf, tokenizer: tokenizer(paragraf) tokenized_dataset = [] for i in berita: tokenized_dataset.append(tokenize_data(i,tokenizer))
masing-masing akan menyimpan token nya dari 1.724 record berita. Kalian bisa melihat tiap2 record mempunyai panjang yang berbeda-beda
inilah nanti yang akan kita gunakan Model Bahasa LSTM Many to Many.
Vocab
Vocab (Vocabulary):
- Definisi: Vocabulary adalah kumpulan kata-kata unik yang ditemukan dalam corpus tertentu.
- Isi: Vocab berisi semua kata-kata unik yang ditemukan dalam corpus, tanpa mengulangi kata yang sama. Ini mencakup kata-kata yang mungkin jarang muncul dan kata-kata yang sangat umum.
- Ukuran: Ukuran vocab biasanya jauh lebih kecil daripada ukuran corpus, terutama jika corpusnya besar. Namun, ukuran vocab dapat bervariasi tergantung pada keragaman dan kompleksitas bahasa dalam corpus tersebut
Dari kumpulan token/kata kita akan buat vocab dengan minimal muncul sebanyak 3 kali akan dimasukan sebagai vocab.
vocab = torchtext.vocab.build_vocab_from_iterator(tokenized_dataset, min_freq=3)
tag <unk> atau unknown dan <eos> atau end of statement
Selain vocab yang dihasilkan oleh token diatas, kita juga akan menambahkan token khusus yaitu
- <unk> atau unknown dan
- <eos> atau end of statement alias akhir kalimat
vocab.insert_token('<unk>', 0) #tambah kosa kata unknown vocab.insert_token('<eos>', 1) #tambah kosa kata end on sentence vocab.set_default_index(vocab['<unk>'])
mari kita cek vocab nya dari 0 sampai 10
vocab.get_itos()[0:10] Out[47]: ['<unk>', '<eos>', '.', ',', 'yang', 'di', 'dan', 'com', 'juga', 'kompas']
Nanti setiap akhir record kita akan tambahkan <eos> sebagai penanda akhir artikel. <eos> disini menandakan sudah beda artikel!
Mengubah Token menjadi Array
Mesin komputer hanya mengenal angka sehingga kita akan mengubah menjadi angka/array dalam bentuk index. Sebelumnya kita sudah mempunyai kumpulan token yang disebut dengan Vocab. Mari kita cek sebuah token/kata dari “merilis”. No berapakah “merilis” di dalam vocab?
vocab["merilis"]
ternyata no index 2.228
mari kita hitung semua token yang ada di variabel list tokenized_dataset
jumlah = 0 for t in tokenized_dataset: jumlah+=len(t)
ada sekitar 1.126.636, nanti kita akan split menjadi 128 baris sehingga terdapat 8.801 kolom alias melakukan reshape array
#get data def get_data(dataset, vocab, batch_size): data = [] for example in dataset: example2 = example.copy() #buat mencegah by reference example2.append('<eos>') #setiap akhir kalimat kasih tanda <eos> index_tokens = [vocab[token] for token in example] data.extend(index_tokens) data = torch.LongTensor(data) num_batches = data.shape[0] // batch_size #pembulatan data = data[:num_batches * batch_size] data = data.view(batch_size, num_batches) #lakukan reshape return data batch_size = 128 train_data = get_data(tokenized_dataset, vocab, batch_size)
kita cek train_data
print(train_data.shape)
hasilnya
torch.Size([128, 8801])
Membuat model bahasa
Mari kita buat model bahasa dengan model LSTM berjenis many to many
class LSTM(nn.Module): def __init__(self, vocab_size, embedding_dim, hidden_dim, num_layers, dropout_rate, tie_weights): super().__init__() self.num_layers = num_layers self.hidden_dim = hidden_dim self.embedding_dim = embedding_dim self.embedding = nn.Embedding(vocab_size, embedding_dim) self.lstm = nn.LSTM(embedding_dim, hidden_dim, num_layers=num_layers, dropout=dropout_rate, batch_first=True) self.dropout = nn.Dropout(dropout_rate) self.fc = nn.Linear(hidden_dim, vocab_size) if tie_weights: assert embedding_dim == hidden_dim, 'cannot tie, check dims' self.embedding.weight = self.fc.weight self.init_weights() def forward(self, src, hidden): embedding = self.dropout(self.embedding(src)) output, hidden = self.lstm(embedding, hidden) output = self.dropout(output) prediction = self.fc(output) return prediction, hidden def init_weights(self): init_range_emb = 0.1 init_range_other = 1/math.sqrt(self.hidden_dim) self.embedding.weight.data.uniform_(-init_range_emb, init_range_emb) self.fc.weight.data.uniform_(-init_range_other, init_range_other) self.fc.bias.data.zero_() for i in range(self.num_layers): self.lstm.all_weights[i][0] = torch.FloatTensor(self.embedding_dim, self.hidden_dim).uniform_(-init_range_other, init_range_other) self.lstm.all_weights[i][1] = torch.FloatTensor(self.hidden_dim, self.hidden_dim).uniform_(-init_range_other, init_range_other) def init_hidden(self, batch_size, device): hidden = torch.zeros(self.num_layers, batch_size, self.hidden_dim).to(device) cell = torch.zeros(self.num_layers, batch_size, self.hidden_dim).to(device) return hidden, cell def detach_hidden(self, hidden): hidden, cell = hidden hidden = hidden.detach() cell = cell.detach() return hidden, cell
kita panggil dan hitung paramater nya ada berapa juta
model = LSTM(vocab_size, embedding_dim, hidden_dim, num_layers, dropout_rate, tie_weights).to(device) optimizer = optim.Adam(model.parameters(), lr=lr) criterion = nn.CrossEntropyLoss() num_params = sum(p.numel() for p in model.parameters() if p.requires_grad) print('Jumlah paramater model: ',num_params)
hasil
Jumlah paramater model: 31.837.525
Model Bahasa untuk Memprediksi Kalimat Selanjutnya
Model bahasa untuk memprediksi kalimat selanjutnya sebenarnya mudah misalkan kita mempunyai dataset
1,2,3,4,5,6,7,8,9,10
maka kita akan split dengan lag 1, misalkan panjang_kata = 7
no index = 1
- input berupa 1 sd 7
- output 2 sd 8
no index = 2
- input berupa 2 sd 8
- output 3 sd 9
no index = 3
- input berupa 3 sd 9
- ouput 4 sd 10
dan seterusnya, function tersebut dikerjakan oleh
def get_batch(data, seq_len, idx): src = data[:, idx:idx+seq_len] target = data[:, idx+1:idx+seq_len+1] return src, target
Fungsi Training
membuat fungsi training, berikut kode lengkap dari function training
def train(model, data, optimizer, criterion, batch_size, seq_len, clip, device): epoch_loss = 0 model.train() num_batches = data.shape[-1] #jumlah kolom data = data[:, :num_batches - (num_batches -1) % seq_len] #untuk memastikan yang diambil yang kalimat pertama num_batches = data.shape[-1] #jumlah kolom hidden = model.init_hidden(batch_size, device) for idx in tqdm(range(0, num_batches - 1, seq_len), desc='Training: ',leave=False): # The last batch can't be a src optimizer.zero_grad() hidden = model.detach_hidden(hidden) #hitung per batch src, target = get_batch(data, seq_len, idx) src, target = src.to(device), target.to(device) batch_size = src.shape[0] prediction, hidden = model(src, hidden) prediction = prediction.reshape(batch_size * seq_len, -1) target = target.reshape(-1) loss = criterion(prediction, target) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), clip) optimizer.step() epoch_loss += loss.item() * seq_len # print("idx: ",idx) return epoch_loss / num_batches
Seperti biasa kita perlu checkpoint, bila ada sesi sebelumnya kita bisa loading
n_epochs = 50 seq_len = 50 clip = 0.25 saved = True #simpan checkpoint folder = "checkpoint" import os if MODE_CHECKPOINT: if os.path.exists(folder+'/model.pt'): print("menggunakan checkpoint") #loading checkpoint if platform=='darwin': checkpoint = torch.load(folder+'/model.pt') else: checkpoint = torch.load(folder+'/model.pt',map_location="cpu") #karena MPS error euy https://github.com/Lightning-AI/pytorch-lightning/issues/17618 model.load_state_dict(checkpoint['model_state_dict']) optimizer.load_state_dict(checkpoint['optimizer_state_dict']) print("loading check point") else: print("tidak ada checkpoint") else: print("tidak menggunakan checkpoint")
Mari kita lakukan training
epoch = 1 for epoch in range(n_epochs): train_loss = train(model, train_data, optimizer, criterion, batch_size, seq_len, clip, device) print(f'\tTrain Perplexity: {math.exp(train_loss):.3f}') if(os.path.exists(folder)==False): os.mkdir(folder) torch.save({ 'model_state_dict': model.state_dict(), 'optimizer_state_dict': optimizer.state_dict() },folder+'/model.pt')
lakukan epoch sampai 50, ditandai dengan nilai loss yang semakin turun.
Model bahasa untuk memprediksi Kalimat selanjutnya
Kita akan membuat prediksi kalimat selanjutnya untuk model bahasa
def generate(prompt, max_seq_len, probablitas, model, tokenizer, vocab, device, seed=None): if seed is not None: torch.manual_seed(seed) model.eval() tokens = tokenizer(prompt) #kalimat akan diubah menjadi perkata indices = [vocab[t] for t in tokens] #ubah kata menjadi array angka berdasarkan database vocab batch_size = 1 # mengingat ini adalah 1 kalimat, maka dianggap 1 batch saja hidden = model.init_hidden(batch_size, device) # jangan lupa hidden state nya with torch.no_grad(): for i in range(max_seq_len): src = torch.LongTensor([indices]).to(device) prediction, hidden = model(src, hidden) probs = torch.softmax(prediction[:, -1] / probablitas, dim=-1) prediction = torch.multinomial(probs, num_samples=1).item() #saat ditemui kosa kata UNKNOWN alias tidak diketahui while prediction == vocab['<unk>']: #cari kata yang paling mendekati prediction = torch.multinomial(probs, num_samples=1).item() #artinya akan berhenti jika ditemui akhir kalimat! if prediction == vocab['<eos>']: break indices.append(prediction) itos = vocab.get_itos() tokens = [itos[i] for i in indices] return tokens
Mari gunakan
def prediksi(prompt,model,tokenizer,vocab,device): print("kata: ",prompt) max_seq_len = 300 seed = 0 model.eval() probablitas = 1 generation = generate(prompt, max_seq_len, probablitas, model, tokenizer, vocab, device, seed) print(str(temperature)+'\n'+' '.join(generation)+'\n') prompt = 'harga mobil tertinggi' prediksi(prompt,model,tokenizer,vocab,device)
hasil prediksinya tidak terlalu tepat
harga mobil tertinggi mulai rp 532 juta pada maret jakarta . dapatkan update berita pilihan dan breaking news setiap hari dari kompas . com . mari bergabung di grup telegram kompas . com news update , caranya klik link https //t . me/kompascomupdate , kemudian join . anda harus install aplikasi telegram terlebih dulu di ponsel . jakarta , kompas . com – asosiasi mitsubishi motors krama yudha sales indonesia ( mmksi ) memperkenalkan produk baru di indonesia , toyota baru telah membawa gaikindo indonesia international auto show ( giias ) 2022 pada kamis ( 15/2/2024 ) . acara ini merupakan salah satu bmw yang menyayangkan soal informasi , yakni local purchase 12 persen yang resmi uji emisi di indonesia karena mencegah kemacetan di area throttle , ” ucap dalam virtual saat dihubungi kompas . com , rabu ( 9/1/2024 ) . dok . tam daftar harga xpander hybrid pada tahun 2019 , produk terendah mulai jadi rp 190 . 500 . 000 gls 125 fi rp 58 . 500 . 000 hingga rp 150 . 000 . 000 kayak xpander hybrid memang sudah memiliki alasan tersendiri dari presiden direktur rossi , untuk memberikan komentar kepada sebagian orang yang ingin mengikuti arahan pada kawasan lumpur . budi menambahkan , moge digunakan di jalan alternatif sebagai kebutuhan masyarakat yang baru , seperti integrasi beban transportasi secara online dan kelonggaran . seperti diketahui , adanya tes di luar negeri indonesia seperti diketahui , valentino rossi tidak perlu khawatir saat penyelenggaraan iims 2022 dan sangat serius seperti apa . saat saya jajal di kawasan mereka , salah satunya adalah ia menjelaskan , semua pendekatan perusahaan tersebut datang . karena itu , ia mengaku jadi sumber di momen-momen beruntung masyarakat terhadap dan tidak adanya tipu . kalau saya merawat helm dan tangguh , standar dari penggerak roda
Hal ini dikarenakan
- tidak melakukan cleaning data
- kurang nya database
Referensi:
https://towardsdatascience.com/build-your-own-transformer-from-scratch-using-pytorch-84c850470dcb
https://towardsdatascience.com/language-modeling-with-lstms-in-pytorch-381a26badcbf
Kode sumber
Nanti akan dilampirkan kode sumber nya
Berikut source code nya, yang akan tampil jika kalian telah login yang terdiri dari
- login.py
- utils.py
- training.py
Source Code diatas akan tampil jika kalian login!