Implementasi Jaringan Saraf Tiruan menggunakan PyTorch

Pada artikel ini kita akan coba melakukan implementasi Jaringan Saraf Tiruan / JST / Artificial Neural Network untuk kasus klasifikasi menggunakan PyTorch. Bagi yang belum tahu PyTorch bisa membaca artikel sebelumnya di sini atau bagi yang mau memahami bagaimana JST bekerja bisa buka artikel berikut.

Dataset

Kita akan menggunakan dataset Iris yang sudah cukup populer. Dataset ini berisi data dari 150 Bunga Iris yang berasa dari 3 spesies berbeda. Diberikan 4 buah fitur / ciri, yakni panjang kelopak (sepal length), lebar kelopak (sepal width), panjang mahkota (petal length), dan lebar mahkota (petal width), lalu kita akan mencoba untuk mengklasifikasikan bunga tersebut adalah spesies apa.

iris set

Saya telah memisahkan dataset aslinya menjadi dua file, satu untuk training, dan satu untuk testing. File csv dataset tersebut dapat di download di sini: training set dan test set.

Implementasi Jaringan Saraf Tiruan

Arsitektur

Melihat dataset yang ada, dapat kita simpulkan untuk arsitektur jaringan saraf tiruan kita membutuhkan 4 buah neuron input (fitur) dan 3 buah neuron output (spesies). Kita akan coba menggunakan sebuah hidden layer dengan jumlah neuron hidden layer sebanyak 3. Ilustrasinya tampak sebagai berikut:

Pre-Processing

Pelatihan dilakukan menggunakan data latih iris_train.csv. Ada sebuah tahapan yang perlu dilakukan sebelum memulai tahapan pelatihan. Tahapan ini disebut pre-processing. Ada banyak hal yang bisa dilakukan pada tahapan ini, tetapi pada tutorual ini saya hanya akan melakukan pre-processing berupa mengubah nilai yang masih string menjadi integer.

Contohnya pada kolom species, kita ubah “Iris-setosa” menjadi 0, “Iris-versicolor” menjadi 1, dan “Iris-virginica” menjadi 2. Kita lakukang ini menggunakan Pandas:

import pandas as pd

# load data train
datatrain = pd.read_csv('../Datasets/iris/iris_train.csv')

# mengubah nilai string menjadi angka dengan pandas
datatrain.loc[datatrain['species']=='Iris-setosa', 'species']=0
datatrain.loc[datatrain['species']=='Iris-versicolor', 'species']=1
datatrain.loc[datatrain['species']=='Iris-virginica', 'species']=2
datatrain = datatrain.apply(pd.to_numeric) # memastikan semuanya numeric

Selain itu, untuk memudahkan diubah juga tipe dari penyimpanan data train tersebut. Yang awalnya adalah Dataframe (karena di-load menggunakan pandas) diubah menjadi Numpy array.

# mengubah tipe dataframe pandas menjadi numpy array
datatrain_array = datatrain.values

Selanjutnya kita pisahkan data tersebut menjadi dua array. Array pertama adalah array fitur, berupa matrix berukuran 120×4 (120 data dengan masing-masing 4 fitur) sedangkan array kedua adalah label, berupa sebuah array 1 dimensi berukuran 120×1 (120 data dengan masing-masing 1 label bernilai antara 0, 1, atau 2)

# memisahkan array fitur dan array label
xtrain = datatrain_array[:,:4]
ytrain = datatrain_array[:,4]

PyTorch Package

Sebelum mulai menggunakan PyTorch, pastikan import semua package yang dibutuhkan. Atur juga seed random agar nilai random tetap sama setiap run-nya.

import torch
import torch.nn as nn
import torch.nn.functional as F
torch.manual_seed(1234)

Membuat Model

Setelah data siap, langkah selanjutnya dalam proses implementasi Jaringan Saraf Tiruan adalah membuat arsitektur/model JST nya. Jika menggunakan PyTorch langkah ini dilakukan dengan cara membuat sebuah class.

Contoh di bawah adalah sebuah class bernama Net yang merupakan sebuah arsitektur JST dengan 4 neuron pada layer input, 3 neuron pada hidden layer, dan 3 neuron pada layer output.

class Net(nn.Module):

    def __init__(self):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(4, 3) # bobot antara input-hidden
        self.fc2 = nn.Linear(3, 3) # bobot antara hidden-output

    def forward(self, x):
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return x
        
modelkita = Net() # menginisalisasi objek

Pada bagian __init__ kita perlu mendefinisikan layer apa saja yang akan digunakan. Pada kasus ini kita hanya akan menggunakan layer Linear / Fully-Connected Layer.

Perlu diperhatikan, bahwa fungsi Linear di sini sebenarnya bukan mewakili sebuah layer, melainkan sebuah matrix bobot yang berada pada antar layer (proses hitungan antar layer). Misalnya, fungsi nn.Linear(4,3) berarti kita mendefinisikan ada sebuah bobot yang memetakan layer dengan neuron sebanyak 4 ke layer dengan neuron sebanyak 3. Jika diilustrasikan maka fungsi tersebut menggambarkan bagian yang ditandai berikut:

Pada fungsi forward, kita definisikan proses forward propagation arsitektur kita. Dari kode di atas, tampak sebuah input x akan dikenai perkalian bobot oleh fc1, lalu dikenai fungsi aktivasi ReLU, lalu dikenai lagi perkalian bobot oleh fc2.

Mendefinisikan Algoritma Optimasi dan Loss Function

Kita tentukan algoritma optimasi apa yang akan kita gunakan pada JST kita. Algoritma optimasi adalah istilah yang digunakan untuk merujuk algoritma yang digunakan untuk memperbaiki eror pada JST. Ada banyak algoritma di PyTorch yang bisa digunakan. Kali ini, kita akan gunakan Stochastic Gradient Descent (SGD) dengan learning rate sebesar 0.01.

optimizer = torch.optim.SGD(modelkita.parameters(), lr=0.01)

Tentukan juga metode untuk menghitung eror yang akan kita gunakan. Untuk kasus ini kita akan menggunakan metode Cross-Entropy.

criterion = nn.CrossEntropyLoss()

Alur Pelatihan

Kita sudah menyiapkan data, membuat model, dan menentukan algoritma-algoritma yang akan kita gunakan. Sekarang saatnya melatih model. Kita latih model dengan data xtrain. Untuk setiap epoch, ada beberapa langkah utama dalam proses pelatihan menggunakan PyTorch:

  1. Ubah semua variabel yang digunakan sudah bertipe Tensor
  2. Inputkan data ke model untuk mendapatkan output model
  3. Hitung eror antara output dengan label
  4. Bersihkan riwayat gradient sebelumnya menggunakan zero_grad()
  5. Hitung gradient eror dengan backward()
  6. Update bobot menggunakan algoritma optimasi

Jika dikodekan maka akan tampak sebagai berikut (dengan contoh menggunakan 50 epoch):

for epoch in range(50):
    # 1.
    X = torch.Tensor(xtrain).float()
    Y = torch.Tensor(ytrain).long()

    out = modelkita(X)              # 2.
    loss = criterion(out, Y)  # 3.
    optimizer.zero_grad()     # 4.
    loss.backward()           # 5.
    optimizer.step()          # 6.

    print ('Epoch [%d/%d] Loss: %.4f' %(epoch+1, 
        num_epoch, loss.item()))

Output yang dihasilkan adalah nilai loss atau eror tiap epoch, seperti di bawah:

Epoch [1/50] Loss: 1.1583
Epoch [2/50] Loss: 1.1536
...
Epoch [49/50] Loss: 1.0022
Epoch [50/50] Loss: 1.0000

Jika diperhatikan proses training kita bisa dibilang berhasil karena setelah 50 epoch nilai loss berhasil turun.

Kita bisa mengecek berapa akurasi model terhadap data train dengan menambahkan kode berikut:

# ambil nilai terbesar setiap kelasnya sebagai prediksi
prediksi = torch.max(out.data, 1)[1] 
# bandingkan dengan label, hitung rata-rata persentase
akurasi = 100 * torch.sum(Y==prediksi).double() / len(Y)
print(akurasi)

Ketika program ini saya run di laptop saya dengan versi PyTorch 1.1 diperoleh akurasi pada data train adalah 70.4762%

Alur Pengujian

Untuk memastikan seberapa baik model kita, maka kita perlu menguji model kita menggunakan data yang berbeda dengan data latih. Kita lakukan pengujian menggunakan iris_test.csv. Potongan kode berikut sama dengan kode-kode di atas sebelumnya, hanya saja kita menggunakan data tes.

# load data tes
datatest = pd.read_csv('../Datasets/iris/iris_test.csv')

# mengubah nilai string menjadi angka dengan pandas
datatest.loc[datatest['species']=='Iris-setosa', 'species']=0
datatest.loc[datatest['species']=='Iris-versicolor', 'species']=1
datatest.loc[datatest['species']=='Iris-virginica', 'species']=2
datatest = datatest.apply(pd.to_numeric)

# mengubah tipe dataframe pandas menjadi numpy array
datatest_array = datatest.values

# memisahkan array fitur dan array label
xtest = datatest_array[:,:4]
ytest = datatest_array[:,4]

# mendapatkan nilai prediksi
X = torch.Tensor(xtest).float()
Y = torch.Tensor(ytest).long()
out = modelkita(X)
prediksi = torch.max(out.data, 1)[1]

# hitung akurasi
akurasi = 100 * torch.sum(Y==prediksi).double() / len(Y)
print('Accuracy of the network %.4f %%' % akurasi)

Di komputer saya, hasil akurasi data tes adalah sebesar 68.88%, sedikit lebih kecil dari pada akurasi data train. Hal ini wajar karena model JST hanya melihat data train saat dilatih.

Apakah akurasi ini masih bisa ditingkatkan? insyaAllah.. silakan coba ganti-ganti arsitektur dan parameter untuk mendapatkan akurasi yang lebih baik (disebut Tuning Parameter). Misalnya dengan mengubah algoritma, jumlah neuron, atau banyak epoch. Beberapa eksperimen harus dilakukan untuk menjadikan akurasi kita benar-benar baik dan valid.

Sekian, semoga bermanfaat, kode lengkap dapat dilihat di sini

About the author

Rian Adam

Lecturer at Universitas Islam Indonesia; Machine Learning Enthusiast

View all posts

Leave a Reply