Convolutional Neural Network (CNN) menggunakan PyTorch

Artikel ini akan langsung berfokus pada implementasi Convolutional Neural Network (CNN) menggunakan PyTorch. Bagi yang ingin memperdalam teori dibalik CNN terlebih dahulu bisa baca pada link artikel sebelumnya yang berisi kumpulan sumber belajar CNN dan jika ingin memperdalam PyTorch, juga bisa baca artikel sebelumnya tentang PyTorch.

Dataset

Kita akan menggunakan Convolutional Neural Network untuk mengklasifikasi citra barang-barang yang ada di sebuah toko (Freiburg Groceries Dataset). Dataset dan code bisa didownload di repository berikut.

Dataset yang digunakan jumlahnya akan lebih sedikit dari dataset asli agar mempercepat proses pelatihan. Dataset pada repo yang kita gunakan hanya terdiri dari 5 kelas, yakni citra produk Susu (MILK), Air mineral (WATER), soda (SODA), jus (JUICE), dan cuka (VINEGAR), dengan sekitar 900-an gambar untuk pelatihan dan 120 gambar untuk pengujian. Semua citra berukuran sama, yakni 256×256 pixel.

Convolutional Neural Network Data
salah satu citra dengan kelas “MILK”

Kita akan menggunakan dataset yang ada pada folder “train” untuk pelatihan dan yang ada pada folder “test” untuk pengujian. Pada tutorial ini kita masih belum menggunakan teknik validasi seperti menggunakan data validasi atau cross-validation.

Pengolahan Dataset di PyTorch

Untuk pengolahan citra ada dua package dari python yang bisa kita gunakan, yakni `torchvision.datasets` dan `torchvision.transforms`

from torchvision import transforms, datasets

Datasets dan Transforms

Kita gunakan `datasets` untuk memudahkan proses pembacaan data. Dengan struktur folder seperti yang ada di repo (terkelompok untuk setiap kelas) maka dengan datasets kita bisa menyimpan data beserta label dengan mudah. Sedangkan `transforms` kita gunakan untuk pengolahan awal dataset.

train_dir = "train/"
test_dir = "test/"

train_transform = transforms.Compose([
    transforms.CenterCrop(150),
    transforms.RandomRotation(5), 
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor(),
    transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
])
test_transform = transforms.Compose([
    transforms.CenterCrop(150),
    transforms.ToTensor(),
    transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
])

train_img = datasets.ImageFolder(train_dir, transform=train_transform)
test_img = datasets.ImageFolder(test_dir, transform=test_transform)

Pada contoh di atas, saya baca dataset pada folder train dan test menggunakan `datasets.ImageFolder()` lalu masing-masingnya kita beri pengolahan yang telah saya definisikan. Misalnya pada data train, pengolahan yang saya lakukan menggunakan `train_transform`:

  1. crop citra menjadi ukuran 150×150
  2. lalu secara random beberapa data saya rotasi 5 derajat
  3. beberapa data juga secara random saya cerminkan secara horizontal
  4. saya ubah citra menjadi tensor 3D (3 x 150 x 150)
  5. saya normalisasi masing-masing channel sehingga rata-rata dan standar deviasinya bernilai 0.5

Setelah terbaca menggunakan `datasets` maka citra-citra kita akan ter-sebagai objek `torch.datasets` yang mirip dengan list of tuple. Setiap tuple bagian pertama adalah tensor citra, dan bagian kedua adalah label citra tersebut.

print("train_img type   :",type(train_img)) # objek datasets
print("train_img length :",len(train_img)) # 940 data train
print("test_img length :",len(test_img)) # 120 data test
print("train_img classes:",train_img.classes) # nama kelas
print("train_img[0] type:",type(train_img[0])) # tuple
print("train_img[0][0] s:",train_img[0][0].size()) # tensor citra
print("train_img[0][1]  :",train_img[0][1]) # label

Objek datasets tersebut, kita ubah bentuknya menjadi `DataLoader` untuk memudahkan proses batch.

trainloaders = torch.utils.data.DataLoader(train_img, batch_size=64, shuffle=True)
testloaders = torch.utils.data.DataLoader(test_img, batch_size=32, shuffle=True)

Implementasi Convolutional Neural Network

Ada 3 bagian utama dalam implementasi CNN menggunakan PyTorch:

  1. Class Model / Arsitektur CNN
  2. Alur pelatihan
  3. Alur pengujian

Arsitektur CNN

Ilustrasi CNN untuk klasifikasi citra ke 3 kelas (hanya contoh, berbeda dengan arsitektur yang akan digunakan pada artikel ini)

Pada tutorial ini, kita akan menggunakan model CNN dengan dua convolutional layer dan sebuah fully connected layer. Dengan detail parameter setiap layer sebagai berikut:

W = window size, S = stride, P = padding, FM = feature map, N = jumlah neuron

Pada proses konvolusi atau pooling, parameter window size, pooling size, stride, dan padding akan menentukan perubahan ukuran data setelahnya. Ukuran data setelah proses konvolusi atau pooling dapat dihitung dengan menggunakan rumus berikut:

$$ UkuranAkhir =\frac{UkuranAwal – W + 2P}{S} + 1 $$

Rumus tersebut dihitung untuk setiap sisi citra. Sebagai contoh di atas, citra memiliki ukuran panjang x lebar (150 x 150). Pada layer Conv1 ukuran awal sisi panjang citra adalah 150. Dikarenakan nilai sisi panjang W = 3, lalu nilai P = 0, dan S = 1, diperoleh sisi panjang citra setelah proses adalah 148. Karena citra input memiliki sisi lebar yang sama, yakni 150, maka sisi lebar citra dihitung dengan cara dan diperoleh Conv1 adalah 148. Sehingga diperoleh hasil proses konvolusi berukuran 148 x 148 x 32 (dari 32 feature maps). Proses ini juga dilakukan di layer Pool1, Conv2, dan Pool2 dengan UkuranAwal adalah UkuranAkhir dari layer sebelumnya.

Pembuatan model pada PyTorch dilakukan dengan menyusun urutan layer dan perhitungan yang akan terjadi. Model yang digunakan biasanya dalam bentuk Class dengan sebuah fungsi `forward(x)` untuk menghitung proses forward propagation

Sebelum mendefinsikan model beberapa library perlu yang diimport untuk pembentukan model, yakni:

import torch
import torch.nn as nn
import torch.nn.functional as F

Lalu untuk membuat modelnya kita buat dengan mendefinisikan class berikut:

class Net(nn.Module):
   
    def __init__(self):
        super(Net, self).__init__()

        self.conv1 = nn.Conv2d(3, 16, kernel_size=4, stride=1, padding=0)
        self.pool = nn.MaxPool2d(kernel_size=3, stride=2, padding=0)
        self.conv2 = nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=0)
        self.fc1 = nn.Linear(39200, 512)
        self.fc2 = nn.Linear(512, 5)
        self.do = nn.Dropout()
    
    def forward(self, x):
        ## Define forward behavior
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.reshape(x.shape[0], -1)
        x = self.do(F.relu(self.fc1(x)))
        x = self.fc2(x)
        return x

Setelah class tersebut jadi, untuk membuat model kita cukup membuat sebuah objek baru dari class tersebut

model = Net()

Alur Pelatihan

Proses pelatihan dilakukan akan per batch dengan bantuan `DataLoader`. Kita definisikan terlebih dahulu algoritma dan parameter pelatihan:

criterion = nn.CrossEntropyLoss() # loss function
optimizer = torch.optim.Adam(model.parameters()) # algoritma backprop
epoch = 20 # iterasi pelatihan

Karena kita akan menggunakan GPU, maka jangan lupa juga pindahkan model kita ke GPU dengan cara berikut:

model = model.to("cuda")

Setelahnya alur pelatihan dilakukan menggunakan kode di bawah. Dapat diperhatikan ada dua iterasi yang terjadi, iterasi pertama untuk banyak epoch sedangkan iterasi kedua menunjukkan proses backpropagation yang dilakukan setiap mini-batch dari data training (`trainloaders`).

model.train()
for i in range(epoch):
    total_loss = 0
    total_sample = 0
    total_correct = 0

    for image, label in trainloaders: # data tiap batch (64 citra)
        # image adalah tensor berukuran 64 x 3 x 150 x 150
        # label adalah tensor berukuran 64 x 1

        image = image.to('cuda') # pindahkan image ke GPU
        label = label.to('cuda') # pindahkan label ke GPU

        out = model(image) # forward propagation
        loss = criterion(out, label) # hitung error

        optimizer.zero_grad() # langkah awal backprop
        loss.backward() # backpropagation
        optimizer.step() # update bobot berdasar algoritma optimizer

        total_loss += loss.item() # untuk merata2 eror
        total_sample += len(label) # untuk merata2 eror dan akurasi
        total_correct += torch.sum(torch.max(out,1)[1]==label).item()*1.0 # untuk merata2 eror akurasi

    print("epoch", i, total_loss/total_sample, total_correct/total_sample) # menampilkan rata2 eror dan akurasi tiap epoch

Jika tidak terjadi masalah maka akan tampak nilai rata-rata eror akan terus menurun dan rata-rata akurasi akan terus meningkat. Saya melakukan pelatihan di laptop saya ASUS A455L dengan GPU Nvidia 920m, lama pelatihan untuk 20 iterasi tidak sampai setengah jam.

epoch 0 0.03158918555746687 0.30638297872340425
epoch 1 0.021654276264474748 0.39787234042553193
epoch 2 0.01974692699757028 0.46702127659574466
...
epoch 18 0.004239923189929191 0.8978723404255319
epoch 19 0.0036402822967539442 0.9287234042553192

Alur Pengujian

Setelah model selesai dilatih, maka kita akan uji dengan data test. Data test yang belum kita gunakan ini saya pilih secara acak dari masing-masing kelas dan tidak ada di data train.

Proses pengujian tidak beda banyak dengan pelatihan, kita cukup memastikan tidak terjadi backpropagation ataupun update bobot pada saat pengujian. Hal ini dilakukan dengan mengubah status model menjadi `eval()` dan menghapus bagian backprop.

model.eval()
total_loss = 0
total_sample = 0    
total_correct = 0

for image, label in testloaders:
    image = image.to(device)
    label = label.to(device)
    
    out = model(image)

    loss = criterion(out, label)
    total_loss += loss.item()
    total_sample += len(label)
    total_correct += torch.sum(torch.max(out, 1)[1] == label).item()*1.0

print("test loss", total_loss/total_sample)
print("test accuracy", total_correct/total_sample)

Jika kode di atas dijalankan akan menampilkan akurasi model kita. Di laptop saya akurasi yang muncul adalah sekitar 70%. Akurasi ini sudah lebih “cerdas” daripada sekadar menebak kelas secara random. Akurasi ini sudah cukup bagus menurut saya mengingat data yang cukup sulit dan model yang sangat simpel.

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.

Kita akan coba meningkatkan akurasi model kita dengan beberapa teknik di artikel selanjutnya 🙂

About the author

Rian Adam

Lecturer at Universitas Islam Indonesia; Machine Learning Enthusiast

View all posts

Leave a Reply