Bahasa Python
# Mengimpor library yang diperlukan
import numpy as np
import matplotlib.pyplot as plt
# Mendefinisikan class baru dengan nama Judi
class Judi(object):
def __init__(self, m):
self.m = m
self.mean = 0
self.N = 0
def mainkan(self):
return np.random.randn() + self.m
def update(self, x):
self.N += 1
self.mean = (1 - 1.0/self.N)*self.mean + 1.0/self.N*x
def run_experiment(m1, m2, m3, eps, N):
MesinBandit = [Judi(m1), Judi(m2), Judi(m3)]
data = np.empty(N)
for i in range(N):
# epsilon greedy
p = np.random.random()
if p < eps:
j = np.random.choice(3)
else:
j = np.argmax([b.mean for b in MesinBandit])
x = MesinBandit[j].mainkan()
MesinBandit[j].update(x)
# Untuk visualisasi nantinya
data[i] = x
cumulative_average = np.cumsum(data) / (np.arange(N) + 1)
# Visualisasi dengan skala logaritmik
plt.plot(cumulative_average)
plt.plot(np.ones(N)*m1)
plt.plot(np.ones(N)*m2)
plt.plot(np.ones(N)*m3)
plt.xscale('log')
plt.show()
for b in MesinBandit:
print(b.mean)
return cumulative_average
if __name__ == '__main__':
c_1 = run_experiment(1.5, 2.25, 2.7, 0.1, 500000)
c_05 = run_experiment(1.5, 2.25, 2.7, 0.05, 500000)
c_01 = run_experiment(1.5, 2.25, 2.7, 0.01, 500000)
# Visualisasi dengan skala logaritmik
plt.plot(c_1, label='eps = 0.1')
plt.plot(c_05, label='eps = 0.05')
plt.plot(c_01, label='eps = 0.01')
plt.legend()
plt.xscale('log')
plt.show()
# Visualisasi berapa lama agen bisa converge (mendapat solusi terbaik)
plt.plot(c_1, label='eps = 0.1')
plt.plot(c_05, label='eps = 0.05')
plt.plot(c_01, label='eps = 0.01')
plt.legend()
plt.show()
Penjelasan:
- Line 2-3 mengimpor library yang diperlukan.
- Line 6 mendefinisikan class dengan nama Judi. Disclaimer : nama Judi hanya penamaan saja ya. Saya tidak merekomendasikan bermain judi.
Penulisan class Judi sama dengan penulisan class Judi(object). Di Python 3 kita tidak perlu menuliskan object lagi setelah mendefinisikan sebuah class. Mengapa saya tambahkan object di sini? Karena barangkali ada pembaca yang masih menggunakan Python versi 2.
- Line 7 mendefinisikan method __init__. Method ini digunakan untuk mendefinisikan attribute yang ada di class Judi. Method __init__ hanya boleh ada 1 untuk setiap class yang kita buat. Penulisannya diikuti dengan menuliskan self sebagai referensi objek di class yang digunakan nantinya. Self ini wajib ada. Kemudian diikuti dengan m yang merupakan nama objeknya yang akan dipakai saat eksekusi. Artinya saat memanggil class Judi, maka ia butuh minimal 1 parameter, yaitu m.
- Line 8 kita mendefinisikan m yang merupakan nilai rataan (reward) yang sesungguhnya dari sebuah mesin.
- Line 9 kita mendefinisikan mean yang merupakan nilai estimasi dari rataan sebuah mesin bandit. Perlu dibedakan, line 8 adalah nilai reward yang sebenarnya (dalam hal ini ceritanya agen tidak tahu nilai ini). Sementara line 9 adalah nilai estimasi reward (dalam hal ini ceritanya agen belajar mengestimasi nilai reward dari permainan yang sudah ia lalui). Kita setting nilai awal mean = 0.
- Line 10 kita mendefinisikan N yang merupakan informasi iterasi ke berapa agen ini berada. Untuk tahap awal (inisiasi) kita setting N = 0.
- Line 12 kita definisikan method mainkan. Di sini kita memainkan salah satu mesin.
- Line 13 kita tentukan bahwa perintah di line 12 akan menghasilkan bilangan random yang bersifat normal (distribusi normal/Gaussian) ditambahkan dengan nilai m (reward) asli dari mesin tersebut.
- Line 15 kita definisikan method update. Di sini kita mengupdate nilai mean yang baru untuk mesin yang dijalankan. Attribute yang kita definisikan adalah x.
- Line 16 mengupdate nilai N untuk bertambah 1. Artinya iterasinya bertambah 1, dari State S(t) menjadi S(t+1).
- Line 17 mengupdate nilai estimasi rataan. Dengan demikian, setiap sebuah mesin dimainkan, nilai estimasinya akan terupdate secara otomatis. Sudah kita ketahui bahwa rumus rataan adalah sebagai beriku:

Namun kali ini, kita sederhanakan rumusnya melalui proses iterasi, sehingga menjadi:

Mengapa kita gunakan rumus rataan yang baru? Karena dalam RL kita hanya tahu nilai rataan t dan t-1, sehingga rumus awal yang umum tidak bisa digunakan.
- Line 20 mendefinisikan fungsi (bukan method ya, karena ia bukan bagian dari class manapun, perhatikan indentasinya) run_experiment yang nantinya akan menjalankan eksperimen permainan K-armed bandit. Attributes yang diperlukan adalah m1, m2, m3, eps, dan N. Nilai m1, m2, dan m3 adalah nama mesinnya. Tentu saja untuk pemilihan namanya bebas, dan kali ini jumlahnya adalah 3 mesin (pembaca bisa membuat lebih dari 3 mesin). Kemudian eps adalah nilai epsilon greedy yang diinginkan. N adalah nilai iterasi (berapa kali kita coba bermain).
- Line 21 mendefinisikan MesinBandit sebagai sebuah list yang berisikan ketiga mesin tersebut. Nantinya program akan mengiterasi ketiga mesin ini. Jika nilai bilangan random kurang dari epsilon greedy maka mainkan mesin secara random. Jika lebih dari epsilon maka lakukan eksploitasi. Ide dasarnya adalah sebagai berikut:
p = random()
if p < epsilon:
eksplorasi
else:
eksploitasi
- Line 23 mendefinisikan data sebagai variabel kosong. Nantinya variabel data ini digunakan untuk memvisualisasikan hasilnya.
- Line 25-36 adalah looping untuk permainan K-armed bandit. Looping terus berlangsung sampai jumlah iterasi yang kita tentukan.
- Line 27 membangkitkan bilangan random dari 0-1.
- Line 28-31 adalah penjabaran teknis dari konsep yang dibahas di line 21.
- Line 28, jika p lebih kecil dari epsilon maka pilih mesin yang ada di list MesinBandit yaitu apakah mesin 0, 1 atau 2 (dalam Python, indeks dimulai dari nol).
- Line 31, jika line 29 tidak terpenuhi maka pilih mesin yang memiliki nilai estimasi rataan tertinggi dengan menggunakan fungsi np.argmax. Perlu diperhatikan b.mean referensinya ada di line 17 (saya harap pembaca tidak bingung).
- Line 32 mengeksekusi mesin yang terpilih (tergantung apakah line 29 atau 31 yang terpilih).
- Line 33 melakukan proses update nilai estimasi mean untuk mesin yang terpilih di line 32.
- Line 36 memasukkan nilai x ke dalam variabel data yang sudah didefinisikan sebelumnya di line 23.
- Line 38 menghitung nilai rataan akumulasi. Di sini setiap state (iterasi yang sudah dilalui) memiliki nilai rataannya masing-masing. Fungsi np.cumsum artinya menjumlahkan sebuah kolom dengan kolom-kolom sebelumnya. Fungsi np.arange adalah menjabarkan urutan. Penulisan np.arange(3) akan menghasilkan list array([0,1,2]). Kenapa dijumlahkan satu di akhir, karena indeks python dimulai dari nol, maka kita tambahkan 1.
- Line 41-46 visualisasi dari hasi cumulative_average di line 38. Fungsi np.ones di line 42-44 adalah untuk membuat garis lurus horisontal nilai m untuk masing-masing mesin. Line 45 menjelaskan bahwa skala untuk sumbu x adalah logaritmik.
- Line 48-49 akan mencetak nilai estimasi rataan dari masing-masing mesin.
- Line 51 mengembalikan nilai cumulative_average dari fungsi run_experiment.
- Line 54-65 adalah perintah untuk mengeksekusi program.
- Line 54 adalah teknik di python agar bisa mengeksekusi program. Semua line di atas line 54 adalah proses definisi saja. Walaupun sudah didefinisikan, semuanya tidak akan dijalankan kalau memang tidak ada perintah run. Untuk bisa melakukan run semua proses definisi sebelumnya, maka kita tuliskan if __name__ == ‘__main__’. Mengapa demikian, karena setiap algoritma yang dieksekusi saat itu, pasti namanya (__name__) di balik layar akan menjadi ‘__main__’. Kita hanya memastikan saja, keduanya sama. Ini juga menunjukkan bahwa yang dieksekusi adalah script atau algoritma yang ditulis di editor spyder, dan bukan script yang diimpor di luar. Ini hanya teknis Python saja, semoga pembaca tidak bingung ya =)
- Line 55-57 adalah 3 jenis eksperimen dengan nilai epsilon yang berbeda yaitu 0.1, 0.05, dan 0.01. Nilai iterasinya adalah 500rb kali uji coba. Hasil visualisasinya tampak sebagai berikut:

Pada gambar di atas bisa kita lihat ada 3 epsilon yang berbeda, yaitu 0.1, 0.05, dan 0.01. Masing-masing kurva epsilon memiliki warnanya masing-masing. Kemudian bisa dilihat pula ada 3 garis horisontal. Garis-garis horisontal ini menandakan nilai reward dari masing-masing mesin (1.5, 2.25, dan 2.75).
Ketiga kurva di masing-masing epsilon menunjukkan pola mesin mana yang dipilih. Yang paling penting adalah bisa kita lihat bahwa lama-lama ketiga kurvanya bertemu di garis horisontal 2.75. Artinya semua kurva epsilon memutuskan untuk memainkan mesin yang memiliki reward 2.75 karena memang ia menawarkan reward yang tertinggi. Dan dalam jangka waktu yang panjang, ketiga kurva stabil di rentang 2.75 ini. Bisa juga dilihat nilai epsilon 0.01 yang paling cepat converge.
Hasil akhir pada kurva yang stabil ini dalam bahasa RL kita sebut dengan istilah converge, yang artinya agen sudah menemukan titik yang paling optimum.
Perlu diperhatikan bahwa kurva ini memiliki unsur bilangan random, sehingga pembaca bisa saja mendapatkan kurva yang tidak sama. Namun secara garis besar jika eksperimen ini dilakukan berkali-kali hasilnya akan sama. Kurva di atas merupakan hasil dari 1 kali eksperimen (istilahnya adalah 1 kali epoch). Pembaca bisa juga melakukan beberapa epochs sehingga bisa mendapatkan hasil yang lebih stabil. Istilah epoch akan banyak kita pakai di pembahasan deep learning nantinya.
Sekarang kita ingin lihat, berapa lama yang diperlukan agar bisa converge? Untuk bisa melihatnya kita cukup mengeksekusi line 68-72. Hasilnya tampak sebagai berikut:

Ternyata tidak dibutuhkan waktu yang lama. Bahkan tidak sampai uji coba yang ke 100ribu. Jika pembaca melakukan dragging mouse di kurva yang dihasilkan spyder, maka akan dilihat kurang lebih pada iterasi ke 1000, kurvanya sudah converge.
Sampai di sini saya harap pembaca paham bahwa teknik reinforcement learning bisa dipakai untuk memecahkan masalah K-armed bandit dan turunannya (misal: proses pemilihan iklan yang tepat). Agen akan belajar dari proses uji coba (eksplorasi) dan dari situ ia harus segera mengambil langkah yang tepat (eksploitas) sehingga dalam waktu yang singkat reward yang didapat maksimum.
Ke depan, kita akan membahas aplikasi RL yang lebih kompleks. Kita juga akan membahas tentang konsep deep learning dan nantinya akan kita gabungkan keduanya menjadi deep reinforcement learning.
Terima kasih, semoga bermanfaat dan terus pantau website saya untuk mendapatkan update materi ML atau AI yang lainnya!
