Membuat Model Bahasa menggunakan Deep Learning

By | February 19, 2024
Print Friendly, PDF & Email
1,196 Views

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

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

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
New User Registration
Really Simple CAPTCHA is not enabled




Enter Captcha Here :

*Required field

Source Code diatas akan tampil jika kalian login!

 

 

Leave a Reply