Siri Belajar AI: Mari Buat Generator Nama Arab (asas model bahasa)
Dalam siri kali ini kita akan cuba membuat semula makemore seperti yang telah diajar oleh Andrej Kaparthy. Dalam tutorial tersebut, Andrej Kaparthy menunjukkan bagaimana GPT AI itu sendiri berfungsi. Ia hanya satu proses statistik yang meramalkan token seterusnya. Ketika satu data teks diproses ia melihat kepada kebarangkalian sesuatu token datang selepas token itu mengikut urutan. Dengan cara itu, ia dapat melengkapkan sesuatu ayat atau teks, selepas sahaja ia diprompt(didorong). Untuk tutorial ini, kita akan membuat Makemore, satu bentuk model bahasa yang ringkas yang boleh meramalkan aksara seterusnya.
Data boleh di muat turun dari Muslim names (kaggle.com)
#mula-mula kita muat naik dulu data yang kita ada
import pandas as pd
import numpy as np
df = pd.read_csv('/kaggle/input/muslim-names/arabnames.csv')
df.info()#kemudian kita masukkan semua nama ke dalam satu senarai (list)
nama = df['Name']
nama[:10]Mari menganalisa dataset kita
Dataset yang kita ambil di atas merupakan dataset nama-nama dalam bahasa Arab. Saya sendiri mengumpulkan senarai nama ini dari beberapa sumber dan meletakkannya dalam satu dataset.
#cuba kita simpan senarai nama ini ke dalam satu susunan aksara. ikut teori susunan aksara ini akan menjadi satu senarai huruf konsonan
senarai_aksara = sorted(list(set(''.join(nama))))
senarai_aksaraNampaknya ada sesuatu yang tak diingini termasuk dalam senarai aksara kita. Senarai aksara ini sepatutnya hanya terdiri dari huruf-huruf dalam bahasa inggeris. Jadi aksara seperti tanda noktah, pembuka dan penutup karangan tak sepatutnya ada. Mari kita buang dulu aksara-aksara yang pelik ini.
# kita simpan aksara yang kita nak buang ini dalam satu variable
aksara_buangan = "()/."
# fungsi untuk buang aksara buangan ini
def buang_aksara(s):
"""
Fungsi ini memasukkan aksara ke dalamnya dan menyambungkan semua aksara itu jika
ia tak termasuk dalam senarai aksara buangan
"""
return ''.join(char for char in s if char not in aksara_buangan)
# kita gunakan fungsi ini
nama_j = [buang_aksara(n) for n in nama]
#sekarang mari kita tengok adakah aksara buangan ini sudahpun terbuang
aksara = sorted(list(set(''.join(nama_j))))
aksaraPara pembaca boleh nampak yang senarai aksara yang kita ada terlepas satu huruf. Huruf apa tu? Huruf x. Barangkali dalam nama arab tidak ada menggunakan huruf x. Tak mengapalah, cuma kita perlu cakna pada jumlah aksara yang kita ada. Kita hanya ada 25 sahaja aksara.
#mari kita tengok pula jumlah nama yang ada di dalam senarai nama kita
len(nama)#kita ada 4511 nama. mari kita cuba tengok jumlah aksara minimum dan maksimum yang ada pada senarai nama kita
min_len = min(len(ak) for ak in nama)
max_len = max(len(ak) for ak in nama)
print(f"Aksara minimum: {min_len}, Aksara maksimum: {max_len}")Mari kita cari jumlah pasangan aksara
Jadi kita boleh lihat di sini, yang kita ada 2 aksara minimum dan 18 aksara maksimum. Langkah seterusnya adalah untuk melihat jumlah pasangan yang ada dalam senarai nama kita. Dalam model bigram, kita meramalkan apakah aksara seterusnya yang akan keluar jika kita diberikan satu ataupun sejumlah aksara. Ramalan ini dilakukan mengikut statistik kebarangkalian menggunakan model jaringan neural. Dalam model bigram, perkara ini dilakukan dengan membentuk pasangan aksara. Untuk itu, kita perlu tahu dulu berapa banyak pasangan aksara yang kita ada
#untuk menghasilkan nilai pasangan aksara, kita perlu keluarkan dulu pasangan tersebut dari senarai nama kita
#bagi memastikan yang kita tak memaparkan semua 4511 nama, mari kita hadkan hanya kepada 10 nama pertama
for ak in nama[:10]:
for ak1,ak2 in zip(ak,ak[1:]):
print(ak1,ak2)Boleh nampak seperti di atas, di mana kita telah keluarkan semua pasangan aksara dari setiap perkataan. Kita sekarang hanya mahu melihat jumlah pasangan yang ada dan frekuensi pasangan aksara yang paling kerap kita lihat. Nak mudahkan cerita, kita hanya ingin melihat pada statistik jumlah aksara. Satu lagi yang perlu ambil kira adalah aksara khas ataupun token khas. Dalam senarai nama kita, kita perlu ada satu aksara khas yang menandakan permulaan dan pengakhiran perkataan. Ini adalah penting bagi memastikan model kita nanti boleh mengenalpasti pengakhiran dan permulaan perkataan ketika proses latihan.
Dalam model AI (model LLM), mereka biasa meletakkan aksara istimewa ini dengan simbol pengenalan istimewa seperti “< S >” dan “< E >” yang melambangkan ‘start’ dan ‘end’. Kita pakailah “< M >” dan “< A >” untuk ‘mula’ dan ‘akhir’ sebab kita kan Melayu
#untuk itu mari kita simpan semua pasangan (bigram) dalam satu senarai
b = {}
for ak in nama:
#masukkan aksara istimewa <M> dan <A> untuk 'mula' dan 'akhir'
na = ['<M>'] + list(ak) +['<A>']
#kita keluarkan pasangan pasangan yang rapat ini
for ak1,ak2 in zip(na,na[1:]):
bigram = (ak1,ak2)
#pada masa yang sama, mari kita tengok jumlah pasangan yang kita ada
b[bigram] = b.get(bigram,0) + 1#mari kita susun senarai pasangan ini mengikut urutan dari yang tertinggi kepada terendah
sorted(b.items(),key = lambda kv:-kv[1])Kita simpan kiraan pasangan aksara ini dalam tensor Pytorch
Meletakkan jumlah pasangan bigram ini memang boleh, tapi ia susah untuk kita manipulasi dan gunakan dalam membentuk model bahasa. Nampak berterabur dan susah untuk difahami bukan senarai di atas? Jadi seeloknya kita simpan senarai pasangan ini dalam matriks ataupun tensor. Main dengan jaringan neural/ AI ni memerlukan kita biasa menggunakan tensor ataupun matriks. Kalau tengok benda di sebalik GPT itu, di belakangnya hanyalah satu operasi matriks sahaja.
Untuk menggunakan matriks dalam python, kita boleh menggunakan pytorch ataupun tensorflow.
#mula dengan muat turun library pytorch
import torch
#mari kita buat satu tensor yang kosong
#ingat tak kita ada 25 huruf (buang x) tambah karakter istimewa.
N = torch.zeros((26,26),dtype= torch.int32)
N#kita simpan aksara itu ke dalam satu bentuk senarai nombor
num_c = {s:i+1 for i,s in enumerate(aksara)}
num_c['.'] = 0
num_c#mari simpan senarai ini dalam tensor
for ak in cleaned_nama:
#kita tukarkan aksara istimewa dari M dan A kepada hanya titik noktah bagi meringkaskan tensor kita
na = ['.'] + list(ak) + ['.']
for ak1,ak2 in zip(na,na[1:]):
ix1 = num_c[ak1]
ix2 = num_c[ak2]
N[ix1,ix2] +=1
NMari kita visualkan tensor kita
Tapi tengok tensor pun boleh jadi pening jugak, jadi seeloknya kita visualkan tensor ataupun matriks kita dengan bantuan matplotlib.
#kita terbalikkan senarai aksara ke nombor kita untuk memudahkan proses visualisasi
c_num = {i:s for s,i in num_c.items()}
c_num#kita gunakan library matplotlib
import matplotlib.pyplot as plt
%matplotlib inline
#buat satu imej dengan figura 16x16
plt.figure(figsize=(16,16))
#buat satu peta berwarna
plt.imshow(N, cmap = 'Greens')
#senaraikan aksara berserta pasangan
for i in range(26):
for j in range(26):
akstr = c_num[i] + c_num[j]
plt.text(j,i, akstr, ha ="center", va= "bottom", color = "black")
plt.text(j,i,N[i,j].item(), ha = "center",va ="top", color = "black" )
plt.axis('off');Apa yang gambar di atas ini paparkan? Gambar ini hanya menunjukkan aksaras dengan pasangan yang paling kerap keluar. Kita boleh tengok pada baris paling atas. Aksara yang paling kerap keluar adalah aksara a, dan aksara yang paling kerap mengikut aksara a terletak di baris bawah, aksara h. jadi secara keseluruhannya, dalam 4511 nama itu, ada 1192 kali nama itu ada pasangan aksara ah. Jumlah kebarangkaliannya adalah 1192/4511 = 26%.
Mari kita buat generator mudah
Dengan data yang kita ada ini, kita boleh membentuk satu generator yang boleh menghasilkan senarai nama-nama arab yang sesuai. Tensor ataupun tatasusunan (array) yang kita ada sekarang sudah cukup untuk membolehkan kita mengambil (ataupun sampel) karakter-karakter mengikut nilai kebarangkaliannya. Apa yang dipaparkan dalam gambar di atas adalah pasangan karakter berturut dan jumlah ia muncul dalam senarai nama yang kita ada tadi
Apa yang kita boleh buat adalah, dengan nilai kebarangkalian ini, kita boleh membentuk satu generator yang boleh menghasilkan senarai nama mengikut nilai-nilai karakter yang kita ada. Mari kita cuba buat
#kita gunakan satu generator dan tetapkan seed secara manual
g = torch.Generator().manual_seed(433465)
#buat satu ulangan (loop) sebanyak 5 kali
for i in range(5):
#simpan semua aksara dalam satu senarai 'out'
out = []
#mula dengan indeks 0
ix = 0
while True:
#tukarkan nilai tensor kita kepada float, sebab nak kira kebarangkalian
p = N[ix].float()
#dapatkan nilai kebarangkalian
p = p / p.sum()
#kita gunakan torch multinomial untuk keluarkan sample
ix = torch.multinomial(p,num_samples = 1, replacement =True, generator = g).item()
#gabungkan kesemua aksara dalam satu senarai out
out.append(c_num[ix])
#kita pecahkan loop bila ia terjumpa nilai noktah
if ix == 0:
break
#kita cetak hasilnya
print(f"Nama yang dihasilkan oleh AI:{''.join(out)}")Boleh nampak yang nama-nama yang dihasilkan tak lah nampak seperti nama-nama arab, kecuali beberapa seperti wan dan juga sefawwa.
















