Siri Belajar AI :Mari gunakan LSTM untuk ramal harga Bitcoin (Bahagian 2)
Ok, dalam bahagian yang lepas kita dah pun mengenali tentang LSTM (Long short term memory). Ia arkitektur jaringan neural yang digunakan oleh OpenAI untuk mengalahkan 5 permain professional Dota. Kita buat sedikit refresh lah eh, apa ada dalam LSTM.
Perbezaan LSTM dengan jaringan neural yang asas adalah pada konsep memori. Memori yang diperkenalkan dalam LSTM merupakan satu kaedah bagi menyelesaikan masalah gradien yang meletup dan menghilang yang wujud dalam RNN. Kerana itu, memori ini datang dengan pagar/gate. Kita ada forget gate (yang menentukan memori mana yang perlu dilupakan), input gate (memori yang perlu dimasukkan dan diproses) dan output gate (memori yang perlu dikeluarkan untuk proses setersunya).
Ok, dah ingat kan? Senang nak faham? Baik, dalam bahagian seterusnya kita akan menggunakan LSTM pula dengan menggunakan library Pytorch. Memandangkan LSTM ni bagus untuk memproses dan meramalkan data-data berturut ataupun yang berkaitan dengan siri masa (time series), jadi ada eloknya juga kita gunakan LSTM ini untuk meramalkan harga bitcoin. Yelah, kita asyik uji dengan benda tak bermanfaat je, kali ini, kita buat sesuatu yang lebih dekat sikit dengan aplikasi dunia sebenar.
Memuat dan menyediakan data dari Bitcoin
Benda pertama yang kita perlu buat adalah, sediakan data. Nak dapat data dagangan Bitcoin ni tak susah. Ada banyak sumber. Tapi yang paling senang adalah gunakan Yahoo Finance. Sebab Yahoo Finance sudahpun menyediakkan python library untuk kita terus muat turun data dagangan dalam notebook kita. Untuk memasang/install Yahoo Finance python library, kita hanya perlu jalankan kod dibawah
!pip install yfinanceSelepas memasang yfinance, kita boleh menggunakannya untuk memuat turun data dagangan bitcoin. Hanya jalankan kod di bawah, di mana kita akan mengambil column ‘Close’ sahaja, iaitu harga terakhir ketika dagangan ditutup dalam satu-satu unit masa.
#bawa masuk library yfinance
import yfinance as yf
#muat turun data
btc_data = yf.download("BTC-USD", start="2019-01-01", end="2024-01-01") #ambil data bitcoin dari tahun 2019 ke 2024
btc_data = btc_data[['Close']] # pilih column close sahajaOk, data sudahpun dimuat turun, mari kita cuba visualkan data kita.
# Bawa masuk library yfinance
import matplotlib.pyplot as plt
# kita visualkan pakai matplotlib
plt.figure(figsize=(12, 6))
plt.plot(btc_data.index, btc_data['Close'], label="Harga Bitcoin-USD", color='blue')
plt.title("Harga Bitcoin dari 2019 - 2024")
plt.xlabel("Tahun")
plt.ylabel("Harga (USD)")
plt.legend()
plt.grid()
plt.show()Dari graf di atas, kita boleh nampak yang bacaan dagangan bitcoin ini besar, dalam julat puluh-puluh ribu. Ini boleh menimbulkan masalah ketika proses latihan nanti, sebab LSTM dan model-model AI ni model pakai matematik je, dan bila kita masukkan nombor yang besar-besar, jadi pengiraan dia nanti akan lambat, dan mungkin akan mengundang kesalahan yang besar ketika proses latihan.Jadi seeloknya kita normalisekan dulu, atau seragamkan data kita. Untuk itu, kita boleh pakai library sklearn untuk memproses data ni dengan menggunakan scaler. Mari kita install dulu library tersebut
pip install scikit-learnSelepas itu, mari kita normalizekan data kita dahulu dengan kod di bawah. Kita akan menggunakan modul MinMaxSclaer.Scaler ni ambil nilai maksimum dan minimum dan tukarkan kepada 1 dan 0. Nilai di tengah-tengah akan diskalakan antara 0 dan 1
#muatkan dulu library sklearn, kita akan menggunakan library sklearn.preprocessing dan modul MinMaxScaler
from sklearn.preprocessing import MinMaxScaler
pengskala = MinMaxScaler(feature_range=(0, 1)) #scaler ni ambil nilai maksimum dan minimum dan tukarkan kepada 1 dan 0. Nilai di tengah-tengah akan diskalakan antara 0 dan 1
data_diskala = pengskala.fit_transform(btc_data.values)Mari kita tengok data yang sudah diskalakan
#pakai matplotlib seperti biasa
plt.figure(figsize=(12, 6))
plt.plot(btc_data.index, data_diskala, label="Harga sudah diskalakan Bitcoin-USD ", color='orange')
plt.title("Harga Bitcoin-USD yang sudah diskalakan dari 2018 ke 2023")
plt.xlabel("Tahun")
plt.ylabel("Harga dari skala (0 to 1)")
plt.legend()
plt.grid()
plt.show()Ok, langkah seterusnya adalah untuk menyediakan data sebelum dimasukkan ke dalam model LSTM. Macam yang kita dah pelajari dalam siri yang lepas, model LSTM memerlukan input dan juga ia menghasilkan output. Dalam deep learning/machine learning kita panggil input sebagai features dan ouput,sebagai target, atau nilai yang kita perlu ramalkan. Data Bitcoin yang kita muat turun tadi mengandungi 1826 data point, mewakili 1826 hari dalam tempoh 5 tahun. Data ini perlu disusun untuk membolehkan kita membentuk satu model yang boleh meramalkan dengan tepat nilai harga bitcoin bila diberikan data poin yang terdahulu. Untuk itu, model ini kita akan bentuk supaya ia boleh memproses data dua bulan yang lepas, dan meramalkan nilai data pada hari esoknya. Maka, kita perlulah susun data kita dengan memasukkan 60 data poin, dan meramalkan satu nilai akan datang (60 feature, 1 target). Untuk itu, kita boleh gunakan fungsi di bawah:
from torch.utils.data import Dataset
tengok_belakang = 60 #untuk meramalkan data esok, kita perlu tengok 60 hari ke belakang
# susun dataset dengan 60 features, dan 1 target/output
class data_bitcoin(Dataset):
def __init__(self, data, tengok_belakang):
self.data_x, self.data_y = [], [] #kita wujudkan nilai x dan nilai y sebagai satu senarai
for i in range(tengok_belakang, len(data)): #iterasi pada keseluruhan 1826 siri data
self.data_x.append(data[i-tengok_belakang:i, 0]) #untuk x kita ambil 60 data point sebelum nilai terkini
self.data_y.append(data[i, 0]) #tengok dan ambil data dari indeks terkini
self.data_x = np.array(self.data_x, dtype=np.float32) #simpan nilai x dalam bentuk float32
self.data_y = np.array(self.data_y, dtype=np.float32) #simpan nilai y dalam bentuk float32
def __len__(self):
return len(self.data_x) #dengan menggunakan len kita boleh memanggil panjang data kita
def __getitem__(self, index):
return self.data_x[index], self.data_y[index] #membolehkan kita memanggil data ini dengan mudah melalui indeks
#kita sediakan data kita
dataset = data_bitcoin(data_diskala, tengok_belakang)Ok, sebelum kita pergi ke bahagian seterusnya, kita perlulah dulu pisahkan data kita supaya ia boleh dilatih dan diuji. Kita anggap model LSTM ni sebagai budak sekolah. 80% data kita kita berikan kepadanya supaya ia dapat gunakan untuk membuat latih tubi. Kemudian, lagi 20% data kita gunakan untuk membentuk ujian kepada budak sekolah ini. Kerana itulah kita perlu pisahkan data kita kepada data latihan dan juga data ujian.
#muatkan dulu library pytorch util untuk memuatkan data
import torch
from torch.utils.data import DataLoader, Dataset
# Split data into training and testing
saiz_latih = int(len(dataset) * 0.8)
saiz_uji = len(dataset) - saiz_latih
dataset_latih, dataset_uji = torch.utils.data.random_split(dataset, [saiz_latih, saiz_uji])
pemuat_latihan = DataLoader(dataset_latih, batch_size=32, shuffle=True) #kita pecahkan data kita kepada batch yang kecil, dengan 32 batch jumlahnya. Data ni akan disusun rambang (shuffle)
pemuat_ujian = DataLoader(dataset_uji, batch_size=32, shuffle=False)Menyediakan model dan melatih
Ok, data kita dah siapakan. Dah skalakan ikut skala yang lebih kecil, kita dah persiapkan dalam bentuk input dan sasaran (target) dan kita dan pecahkan kepada data untuk meltaih dan data untuk menguji. Kini sudah tiba masanya untuk kita membina model LSTM kita dan mulakan latihan. Pembaca masih ingat lagi tentang bentuk LSTM dalam siri yang lepas? Ok. Kali ini kita akan bina model menggunakan pytorch. Dengan pytorch, langkah pembinaan model LSTM agak mudah. Kita hanya perlu bayangkan secara kasar bagaimana bentuk jaringan neural kita. Kita tahu ia akan mempunyai 2 bahagian. Yang pertama adalah bahagian LSTM yang mempyunyai keadaan tersembunyi (hidden state) dan juga keadaan terkini (current state). Bahagian LSTM akan mengambil input data dan memprosesnya melalui hiddent state dan juga current state. Output dari LSTM akan dihantar ke jaringan neural ringkas bagi mempentuk output ramalan yang kita perlukan.
Dah faham bagaimana? Tak apa, kita buat je dulu model kita, nanti kita akan terangkan lebih lanjut. Pembaca boleh run je kod di bawah:
# mari kita bina model LSTM kita. sebelum tu kita import dulu library yang diperlukan
import torch
import torch.nn as nn
class LSTMModel(nn.Module):
"""
Model LSTM yang mempunyai dua bahagian. Bahagian LSTM dan juga bahagian jaringan nueral
Bahagian LSTM mempunyai keadaan tersembunyi (hidden state) dan keadaan terkini (current state)
Output dari LSTM akan dihantar ke jaringan neural ringkas bagi membentuk output ramalan
yang kita perlukan
"""
def __init__(self, input_dim, hidden_dim, output_dim, num_layers):
super(LSTMModel, self).__init__()
self.lstm = nn.LSTM(input_dim, hidden_dim, num_layers, batch_first=True) #bahagian LSTM
self.fc = nn.Linear(hidden_dim, output_dim) #bahagian jaringan neural ringkas
def forward(self, x):
h0 = torch.zeros(num_layers, x.size(0), hidden_dim).to(device) #keadaan tersembunyi (hidden state)
c0 = torch.zeros(num_layers, x.size(0), hidden_dim).to(device) #keadaan cel (cell state)
out, _ = self.lstm(x, (h0, c0))
out = self.fc(out[:, -1, :])
return outSelepas itu kita pun run juga kod di bawah untuk menghantar model kita ke dalam CPU/GPU
#setkan nilai parameter yang diperlukan
input_dim = 1
hidden_dim = 50
output_dim = 1
num_layers = 2
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") #handat model ke cpu/gpu
modelsaya = LSTMModel(input_dim, hidden_dim, output_dim, num_layers).to(device)Ok, sebelum kita melatih saya nak buat sesuatu yang menarik dulu. Rupanya feature yang saya nak buat ni tak ada dalam versi asal pytorch. Sebab dulu masa saya pakai tensorflow memang ada satu feature yang menarik. Sebaik sahaja kita bina model kita, kita boleh pakai model.summary() untuk tengok arkitekture model yang kita baru bina. Tapi feature ni tak ada secara native dalam pytorch. Terpaksalah kita install satu lagi library. Jalankan kod di bawah:
!pip install torchinfoBila dah install kita boleh import dan jalankan kod di bawah:
from torchinfo import summary
summary(modelsaya)Wow. Menarik kan? Dengan pakai summary, kita boleh tengok model LSTM kita dengan lebih menyeluruh. Kita model tengok yang kita ada dua lapisan, satu lapisan LSTM dan satu lapisan linear, ataupun lapisan jaringan neural yang ringkas. Kita juga boleh nampak yang kita mempunyai 31,051 parameter. Ini mewakili pemberat dan bias yang ada dalam jaringan neural kita. Sebagai perbandingan, GPT 3 mempunyai 175 billion parameter, kita setakat ada 30,000 je. Jauh lagi. Tapi tengok summary ni mengujakan sebab kita boleh tengok perkaitan apa yang kita dah buat sekarang dengan model-model AI yang ada ketika ini. Ok, kita teruskan ke bahagian latihan
Kita setkan dulu nak pakai loss function dan optimizer apa. dalam kes ini, kita pakai MSE (mean squared error) sebagai loss function dan Adam sebagai optimizer
# kita setkan dulu nak pakai loss function dan optimizer apa. dalam kes ini, kita pakai MSE (mean squared error) sebagai loss function dan Adam sebagai optimizer
criterion = nn.MSELoss() #loss function (fungsi kehilangan)
optimizer = torch.optim.Adam(modelsaya.parameters(), lr=0.001) #optimizer kita, Adam dengan kadar pemberlajaran (lr) pada 0.001
#berapa banyak kitaran latihan (epoch)
num_epochs = 10
#kita loopkan
for epoch in range(num_epochs):
modelsaya.train()
for inputs, targets in pemuat_latihan:
inputs = inputs.unsqueeze(-1).to(device) # Add channel dimension
targets = targets.to(device)
outputs = modelsaya(inputs)
loss = criterion(outputs, targets.unsqueeze(1))
optimizer.zero_grad()
loss.backward()
optimizer.step()
print(f"Epoch yang ke-[{epoch+1}/{num_epochs}], dengan loss sebanyak: {loss.item():.4f}")Bila dah dilatih, kita boleh tengok hasil latihan kita seperti di bawah:
Mari kita menguji model kita
Ok, model kita sudahpun dilatih. Sudah tiba masanya kita menguji model kita. Untuk itu mari kita jalankan kod di bawah
#mari uji model kita, kita panggil balik model kita di bawah, dan letakkan nilai prediction
modelsaya.eval()
ramalan, sebenar = [], []
with torch.no_grad():
for inputs, targets in pemuat_ujian:
inputs = inputs.unsqueeze(-1).to(device)
targets = targets.to(device)
outputs = modelsaya(inputs)
ramalan.append(outputs.cpu().numpy())
sebenar.append(targets.cpu().numpy())
ramalan = np.concatenate(ramalan)
sebenar = np.concatenate(sebenar)
# kena tukar balik skala kita tadi, kenapa normal, gunakan kod di nawah
ramalan = pengskala.inverse_transform(ramalan)
sebenar = pengskala.inverse_transform(sebenar.reshape(-1, 1))Selepas itu, kita boleh visualkan data kita…
plt.figure(figsize=(10, 6))
plt.plot(sebenar, label="Harga Sebenar", color='blue')
plt.plot(ramalan, label="Harga Diramalkan", color='orange')
plt.title("Ramalan Harga Bitcoin")
plt.xlabel("Masa")
plt.ylabel("Harga (USD)")
plt.legend()
plt.show()Wow…nampak dekat. Biar betul. Adakah ini bermakna yang kita dah boleh pakai dah benda ni besok. Hahah, tak. Walaupun result yang kita dapat ni ok, perlu diingatkan yang data yang dipaparkan dalam graf di atas sudahpun dishuffle. Bermakna ia tidaklah mengikut betul-betul siri masa yang ada dalam data kita. Apa yang model LSTM buat hanyalah mengambil input dari 60 hari yang sebelumnya dan membuat ramalan harga untuk hari esok. Jadi ada banyak lagi yang perlu diambil kira ketika menggunakan model ini. Tu belum kita sentuh lagi soal overfitting. Anggaplah ini hanya sebagai satu eksperimen untuk pembelajaran.
Ok, semoga kita berjumpa lagi dalam siri yang akan datang….bye…







