04. Computer Vision dengan MNIST Fashion¶
Menurut Wikipedia, Computer Vision adalah bidang ilmu komputer yang berfokus pada membangun mesin yang dapat memahami dan memproses gambar seperti manusia. Dalam hal ini, kita akan menggunakan dataset yang disebut Fashion MNIST. Fashion MNIST adalah dataset yang berisi gambar-gambar pakaian yang berbeda. Dataset ini dibuat untuk menggantikan dataset MNIST yang sudah sangat populer. Dataset MNIST berisi gambar angka yang ditulis tangan. Dataset Fashion MNIST memiliki format yang sama dengan MNIST, yaitu 28x28 pixel dengan 10 kelas yang berbeda. Kita akan menggunakan dataset ini untuk membangun model yang dapat memprediksi kelas dari gambar-gambar pakaian yang berbeda.
Untuk lebih mengetahui tentang dataset Fashion MNIST, silahkan kunjungi situs resminya
Yang akan kita bahas
- Torchvision: adalah library yang berisi dataset dan model-model yang sudah dibuat untuk Computer Vision
- Memuat Dataset: kita akan memuat dataset Fashion MNIST menggunakan Torchvision
- Mempersiapkan Dataset: kita akan mempersiapkan dataset untuk digunakan dalam model dan memuatnya ke dalam DataLoader
- Membuat Model: kita akan membuat model Neural Network untuk memprediksi kelas dari gambar-gambar pakaian
- Evaluasi Model: kita akan mengevaluasi model yang sudah dibuat dengan menghitung akurasi dari model tersebut
- Memodifikasi Model - 1: kita akan memodifikasi model yang sudah dibuat dengan menambahkan non-linearitas pada model
- Memodifikasi Model - CNN: kita akan memodifikasi model yang sudah dibuat dengan menambahkan Convolutional Neural Network pada model
- Memperbandingkan Model: kita akan membandingkan model yang dibuat di awal, dengan model yang sudah dimodifikasi
- Menyimpan dan memuat Model: kita akan menyimpan model yang sudah dibuat dan memuatnya kembali
Torchvision¶
Torchvision adalah sebuah package yang menyediakan dataset dan model untuk deep learning. Torchvision menyediakan dataset untuk FashionMNIST, MNIST, COCO, ImageNet, CIFAR, dan lain-lain. Torchvision juga menyediakan model untuk ImageNet, SqueezeNet, AlexNet, VGG, ResNet, Inception, dan lain-lain. Torchvision menyediakan fungsi untuk memuat dan memproses dataset, serta untuk melakukan augmentasi gambar.
Terdapat beberapa subpackage yang terdapat di dalam torchvision, yaitu:
| Package | Deskripsi |
| --- | --- |
| torchvision.datasets
| menyediakan dataset untuk Computer Vision |
| torchvision.models
| menyediakan model untuk Computer Vision |
| torchvision.transforms
| menyediakan fungsi untuk melakukan transformasi pada gambar |
| torchvision.utils
| menyediakan fungsi untuk membantu dalam visualisasi gambar |
Kita juga akan menggunakan torch.utils.Dataset
dan torch.utils.DataLoader
untuk memuat dataset dan mempersiapkannya untuk digunakan dalam model.
Mengimpor Library¶
Catatan: Pastikan kamu sudah menginstall library yang digunakan, seperti
torch
,torchvision
,matplotlib
, dantorchmetrics
. Jika belum, silahkan install terlebih dahulu dengan menjalankan perintahpip install torch torchvision matplotlib torchmetrics
di terminal.
## Import Library
# PyTorch
import torch
import torch.nn as nn
# Torchvision
import torchvision
from torchvision import datasets
from torch.utils.data import DataLoader
from torchvision.transforms import ToTensor
# Matplotlib
import matplotlib.pyplot as plt
# Torchmetrics
import torchmetrics
# Mengecek versi PyTorch dan Torchvision
print(f"PyTorch versi: {torch.__version__}")
print(f"Torchvision versi: {torchvision.__version__}")
PyTorch versi: 1.13.1 Torchvision versi: 0.14.1
Device Agnostic¶
Seperti biasa, terlebih dahulu kita mengecek device yang tersedia dan memilih device yang akan digunakan untuk training model.
if torch.cuda.is_available():
device = torch.device("cuda")
print("Tersedia GPU")
elif torch.backends.mps.is_available():
device = torch.device("mps")
print("Tersedia MPS Apple Silicon")
else:
device = torch.device("cpu")
print("Tersedia CPU")
Tersedia MPS Apple Silicon
# device = torch.device("cpu")
Memuat Dataset¶
train_dataset = datasets.FashionMNIST(
root="data",
train=True,
download=True,
transform=ToTensor(),
target_transform=None,
)
test_dataset = datasets.FashionMNIST(
root="data",
train=False,
download=True,
transform=ToTensor(),
target_transform=None,
)
Penjelasan:
root
: lokasi dimana dataset akan disimpantrain
: jika bernilaiTrue
, maka dataset yang akan digunakan adalah dataset training. Jika bernilaiFalse
, maka dataset yang akan digunakan adalah dataset testingdownload
: jika bernilaiTrue
, maka dataset akan diunduh dari internet. Jika bernilaiFalse
, maka dataset akan diunduh dari lokasi yang sudah ditentukan diroot
transform
: fungsi yang akan digunakan untuk melakukan transformasi pada dataset. Dalam contoh di atas, kita menggunakan fungsiToTensor()
yang akan mengubah dataset menjadi tipetorch.Tensor
target_transform
: fungsi yang akan digunakan untuk melakukan transformasi pada label dataset. Dalam contoh di atas, kita tidak menggunakan fungsi ini
Selanjutnya, kita akan mencoba untuk menampilkan gambar-gambar dari dataset FashionMNIST
Mengecek Detail dari Dataset¶
sample_image, sample_label = train_dataset[0]
print(f"Ukuran gambar: {sample_image.size()}")
print(f"Label: {sample_label}")
Ukuran gambar: torch.Size([1, 28, 28]) Label: 9
Dapat kita lihat bahwa ukuran gambar adalah 1, 28, 28
. Ini berarti bahwa gambar tersebut memiliki 1 channel, dan ukurannya adalah 28x28 pixel. Kita juga dapat melihat bahwa label dari gambar tersebut adalah 9
. Label tersebut merupakan label dari kelas dari gambar tersebut. Kita dapat melihat label dari kelas tersebut dengan menggunakan dataset.classes
classes = train_dataset.classes
print(f"Kelas: {classes}")
Kelas: ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']
Selanjutnya kita akan mengecek panjang dari dataset training dan dataset testing
print(f"Jumlah data training: {len(train_dataset)}")
print(f"Jumlah data testing: {len(test_dataset)}")
Jumlah data training: 60000 Jumlah data testing: 10000
Bagaimana representasi dari dataset tersebut dalam bentuk torch.Tensor
? Mari kita coba untuk mengeceknya
print(sample_image)
tensor([[[0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000], [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000], [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000], [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0039, 0.0000, 0.0000, 0.0510, 0.2863, 0.0000, 0.0000, 0.0039, 0.0157, 0.0000, 0.0000, 0.0000, 0.0000, 0.0039, 0.0039, 0.0000], [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0118, 0.0000, 0.1412, 0.5333, 0.4980, 0.2431, 0.2118, 0.0000, 0.0000, 0.0000, 0.0039, 0.0118, 0.0157, 0.0000, 0.0000, 0.0118], [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0235, 0.0000, 0.4000, 0.8000, 0.6902, 0.5255, 0.5647, 0.4824, 0.0902, 0.0000, 0.0000, 0.0000, 0.0000, 0.0471, 0.0392, 0.0000], [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.6078, 0.9255, 0.8118, 0.6980, 0.4196, 0.6118, 0.6314, 0.4275, 0.2510, 0.0902, 0.3020, 0.5098, 0.2824, 0.0588], [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0039, 0.0000, 0.2706, 0.8118, 0.8745, 0.8549, 0.8471, 0.8471, 0.6392, 0.4980, 0.4745, 0.4784, 0.5725, 0.5529, 0.3451, 0.6745, 0.2588], [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0039, 0.0039, 0.0039, 0.0000, 0.7843, 0.9098, 0.9098, 0.9137, 0.8980, 0.8745, 0.8745, 0.8431, 0.8353, 0.6431, 0.4980, 0.4824, 0.7686, 0.8980, 0.0000], [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.7176, 0.8824, 0.8471, 0.8745, 0.8941, 0.9216, 0.8902, 0.8784, 0.8706, 0.8784, 0.8667, 0.8745, 0.9608, 0.6784, 0.0000], [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.7569, 0.8941, 0.8549, 0.8353, 0.7765, 0.7059, 0.8314, 0.8235, 0.8275, 0.8353, 0.8745, 0.8627, 0.9529, 0.7922, 0.0000], [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0039, 0.0118, 0.0000, 0.0471, 0.8588, 0.8627, 0.8314, 0.8549, 0.7529, 0.6627, 0.8902, 0.8157, 0.8549, 0.8784, 0.8314, 0.8863, 0.7725, 0.8196, 0.2039], [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0235, 0.0000, 0.3882, 0.9569, 0.8706, 0.8627, 0.8549, 0.7961, 0.7765, 0.8667, 0.8431, 0.8353, 0.8706, 0.8627, 0.9608, 0.4667, 0.6549, 0.2196], [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0157, 0.0000, 0.0000, 0.2157, 0.9255, 0.8941, 0.9020, 0.8941, 0.9412, 0.9098, 0.8353, 0.8549, 0.8745, 0.9176, 0.8510, 0.8510, 0.8196, 0.3608, 0.0000], [0.0000, 0.0000, 0.0039, 0.0157, 0.0235, 0.0275, 0.0078, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.9294, 0.8863, 0.8510, 0.8745, 0.8706, 0.8588, 0.8706, 0.8667, 0.8471, 0.8745, 0.8980, 0.8431, 0.8549, 1.0000, 0.3020, 0.0000], [0.0000, 0.0118, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.2431, 0.5686, 0.8000, 0.8941, 0.8118, 0.8353, 0.8667, 0.8549, 0.8157, 0.8275, 0.8549, 0.8784, 0.8745, 0.8588, 0.8431, 0.8784, 0.9569, 0.6235, 0.0000], [0.0000, 0.0000, 0.0000, 0.0000, 0.0706, 0.1725, 0.3216, 0.4196, 0.7412, 0.8941, 0.8627, 0.8706, 0.8510, 0.8863, 0.7843, 0.8039, 0.8275, 0.9020, 0.8784, 0.9176, 0.6902, 0.7373, 0.9804, 0.9725, 0.9137, 0.9333, 0.8431, 0.0000], [0.0000, 0.2235, 0.7333, 0.8157, 0.8784, 0.8667, 0.8784, 0.8157, 0.8000, 0.8392, 0.8157, 0.8196, 0.7843, 0.6235, 0.9608, 0.7569, 0.8078, 0.8745, 1.0000, 1.0000, 0.8667, 0.9176, 0.8667, 0.8275, 0.8627, 0.9098, 0.9647, 0.0000], [0.0118, 0.7922, 0.8941, 0.8784, 0.8667, 0.8275, 0.8275, 0.8392, 0.8039, 0.8039, 0.8039, 0.8627, 0.9412, 0.3137, 0.5882, 1.0000, 0.8980, 0.8667, 0.7373, 0.6039, 0.7490, 0.8235, 0.8000, 0.8196, 0.8706, 0.8941, 0.8824, 0.0000], [0.3843, 0.9137, 0.7765, 0.8235, 0.8706, 0.8980, 0.8980, 0.9176, 0.9765, 0.8627, 0.7608, 0.8431, 0.8510, 0.9451, 0.2549, 0.2863, 0.4157, 0.4588, 0.6588, 0.8588, 0.8667, 0.8431, 0.8510, 0.8745, 0.8745, 0.8784, 0.8980, 0.1137], [0.2941, 0.8000, 0.8314, 0.8000, 0.7569, 0.8039, 0.8275, 0.8824, 0.8471, 0.7255, 0.7725, 0.8078, 0.7765, 0.8353, 0.9412, 0.7647, 0.8902, 0.9608, 0.9373, 0.8745, 0.8549, 0.8314, 0.8196, 0.8706, 0.8627, 0.8667, 0.9020, 0.2627], [0.1882, 0.7961, 0.7176, 0.7608, 0.8353, 0.7725, 0.7255, 0.7451, 0.7608, 0.7529, 0.7922, 0.8392, 0.8588, 0.8667, 0.8627, 0.9255, 0.8824, 0.8471, 0.7804, 0.8078, 0.7294, 0.7098, 0.6941, 0.6745, 0.7098, 0.8039, 0.8078, 0.4510], [0.0000, 0.4784, 0.8588, 0.7569, 0.7020, 0.6706, 0.7176, 0.7686, 0.8000, 0.8235, 0.8353, 0.8118, 0.8275, 0.8235, 0.7843, 0.7686, 0.7608, 0.7490, 0.7647, 0.7490, 0.7765, 0.7529, 0.6902, 0.6118, 0.6549, 0.6941, 0.8235, 0.3608], [0.0000, 0.0000, 0.2902, 0.7412, 0.8314, 0.7490, 0.6863, 0.6745, 0.6863, 0.7098, 0.7255, 0.7373, 0.7412, 0.7373, 0.7569, 0.7765, 0.8000, 0.8196, 0.8235, 0.8235, 0.8275, 0.7373, 0.7373, 0.7608, 0.7529, 0.8471, 0.6667, 0.0000], [0.0078, 0.0000, 0.0000, 0.0000, 0.2588, 0.7843, 0.8706, 0.9294, 0.9373, 0.9490, 0.9647, 0.9529, 0.9569, 0.8667, 0.8627, 0.7569, 0.7490, 0.7020, 0.7137, 0.7137, 0.7098, 0.6902, 0.6510, 0.6588, 0.3882, 0.2275, 0.0000, 0.0000], [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.1569, 0.2392, 0.1725, 0.2824, 0.1608, 0.1373, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000], [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000], [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000]]])
Mari kita lihat contoh datasetnya secara visual
plt.imshow(sample_image.squeeze(), cmap="gray")
plt.title(classes[sample_label])
Text(0.5, 1.0, 'Ankle boot')
Gambar di atas merupakan contoh gambar dari dataset FashionMNIST. Gambar tersebut memiliki label 9
, yang berarti bahwa gambar tersebut merupakan gambar dari kelas Ankle boot
. Mari coba lihat gambar lainnya
fig, ax = plt.subplots(2, 5, figsize=(10, 5))
for i in range(2):
for j in range(5):
ax[i, j].imshow(train_dataset[i * 5 + j][0].squeeze(), cmap="gray")
ax[i, j].set_title(classes[train_dataset[i * 5 + j][1]])
ax[i, j].axis("off")
Mengapa gambar pada dataset MNIST Fashion terlihat pecah dan tidak terlihat menarik?
Karena dataset MNIST Fashion merupakan dataset yang sederhana, dan digunakan untuk mempelajari dasar-dasar dari Computer Vision
BATCH_SIZE = 32
train_dataloader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=4)
test_dataloader = DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=4)
Kode di atas bertujuan untuk menyiapkan dataset ke dalam dataloader. Dataloader merupakan sebuah fungsi yang akan memuat dataset ke dalam model. Dataloader juga akan membagi dataset menjadi beberapa bagian, dan membagi dataset tersebut menjadi beberapa batch. Dataloader juga akan melakukan shuffle pada dataset, sehingga model yang dibuat tidak akan terbiasa dengan urutan data yang ada.
Dataloader memiliki beberapa parameter, yaitu:
| Parameter | Deskripsi |
| --- | --- |
| batch_size
| ukuran dari batch yang akan digunakan |
| shuffle
| jika bernilai True
, maka dataset akan diacak sebelum dibagi menjadi batch. Jika bernilai False
, maka dataset tidak akan diacak sebelum dibagi menjadi batch |
| num_workers
| jumlah worker yang akan digunakan untuk memuat dataset |
Catatan: Jika
num_workers
bernilai0
, maka dataset akan di-load secara sequential. Jikanum_workers
bernilai1
, maka dataset akan di-load secara parallel. Jikanum_workers
bernilai lebih dari1
, maka dataset akan di-load secara parallel dengan jumlah worker yang ditentukanCatatan: Tips untuk menentukan jumlah worker yang akan digunakan adalah dengan mengecek jumlah CPU yang ada di komputer. Jumlah worker yang dapat digunakan sama dengan jumlah core CPU yang ada di komputer
print(f"Jumlah batch training: {len(train_dataloader)}")
print(f"Jumlah batch testing: {len(test_dataloader)}")
Jumlah batch training: 1875 Jumlah batch testing: 313
Iterasi Dataloader¶
Untuk mencoba satu buah batch dari dataset, kita dapat menggunakan fungsi next()
pada dataloader. Fungsi next()
akan mengembalikan satu buah batch dari dataset. Kita dapat menggunakan fungsi next()
untuk mengecek satu buah batch dari dataset.
Contoh di bawah ini akan menampilkan satu buah batch dari dataset
# iter dataloader
sample_img_iter, sample_label_iter = next(iter(train_dataloader))
print(f"Ukuran gambar: {sample_img_iter.size()}")
print(f"Ukuran label: {sample_label_iter.size()}")
# plot batch (batch = 32)
plt.figure(figsize=(10, 6))
for i in range(32):
plt.subplot(4, 8, i + 1)
plt.imshow(sample_img_iter[i].squeeze(), cmap="gray")
plt.axis("off")
plt.title(classes[sample_label_iter[i]])
Ukuran gambar: torch.Size([32, 1, 28, 28]) Ukuran label: torch.Size([32])
class FashionMnistModel0(nn.Module):
def __init__(self, input_size, num_classes, hidden_size):
super().__init__()
self.layer_stack = nn.Sequential(
nn.Flatten(),
nn.Linear(input_size, hidden_size),
nn.Linear(hidden_size, num_classes),
)
def forward(self, x):
return self.layer_stack(x)
Penjelasan Model V0:
nn.Flatten()
: fungsi yang digunakan untuk mengubah ukuran dari gambar menjadi 1 dimensi, sehingga yang tadinya memiliki ukuran1, 28, 28
menjadi784
nn.Linear()
: fungsi yang digunakan untuk membuat layer linear. Fungsi ini memiliki 2 parameter, yaitu:in_features
: jumlah neuron pada layer sebelumnyaout_features
: jumlah neuron pada layer yang akan dibuat dalam hal ini kita menggunakan dua buah nn.Linear, dimana yang pertama akan memiliki input sebesar ukuran gambar, dan output sebesarhidden_size
. Sedangkan yang kedua akan memiliki input sebesarhidden_size
, dan output sebesarnum_classes
torch.manual_seed(2023)
model_v0 = FashionMnistModel0(input_size=28 * 28, num_classes=len(classes), hidden_size=10)
model_v0.to(device)
FashionMnistModel0( (layer_stack): Sequential( (0): Flatten(start_dim=1, end_dim=-1) (1): Linear(in_features=784, out_features=10, bias=True) (2): Linear(in_features=10, out_features=10, bias=True) ) )
Loss, Optimizer, Accuracy¶
Selanjutnya kita akan membuat fungsi untuk menghitung loss, optimizer, dan accuracy dari model yang telah dibuat.
loss_fn
yang digunakan adalahnn.CrossEntropyLoss()
. Fungsi ini digunakan untuk menghitung loss dari model yang telah dibuat. Fungsi ini akan menghitung loss dari model yang telah dibuat, dan mengembalikan nilai loss yang telah dihitungoptimizer
yang digunakan adalahoptim.SGD()
. Fungsi ini digunakan untuk menghitung nilai gradient dari model yang telah dibuat. Fungsi ini akan menghitung nilai gradient dari model yang telah dibuat, dan mengembalikan nilai gradient yang telah dihitungaccuracy
yang digunakan adalah dari librarytorchmetrics
bernamaAccuracy
. Fungsi ini digunakan untuk menghitung nilai akurasi dari model yang telah dibuat. Fungsi ini akan menghitung nilai akurasi dari model yang telah dibuat, dan mengembalikan nilai akurasi yang telah dihitung
torchmetrics.Accuracy
memiliki beberapa parameter yang dapat kamu lihat di dokumentasi, dalam hal ini ada dua parameter yang digunakan:
num_classes
: jumlah kelas yang ada pada datasettask
: jenis task yang akan digunakan. Dalam hal ini, karena terdapat banyak kelas, maka kita menggunakantask='multiclass'
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model_v0.parameters(), lr=0.1)
accuracy = torchmetrics.Accuracy(task="multiclass", num_classes=len(classes))
Membuat Fungsi Penghitung Waktu Train¶
Supaya kita dapat mengetahui berapa lama waktu yang dibutuhkan untuk melakukan training, kita akan membuat fungsi untuk menghitung waktu training. Fungsi ini akan menghitung waktu training, dan mengembalikan waktu training yang telah dihitung
from timeit import default_timer as timer
def get_print_time(start: float, end: float, device: torch.device = None):
total_time = end - start
print(f"Total waktu: {total_time:.2f} detik")
return total_time
Training Loop¶
Selanjutnya kita akan membuat fungsi untuk melakukan training loop. Fungsi ini akan melakukan training loop, dan mengembalikan nilai loss dan akurasi dari model yang telah dibuat
train_start_time = timer()
EPOCHS = 5
for epoch in range(EPOCHS):
# training
train_loss = 0
# enumerate dataloader
for batch_idx, (data, target) in enumerate(train_dataloader):
model_v0.train()
data, target = data.to(device), target.to(device)
y_pred = model_v0(data)
# hitung loss
loss = loss_fn(y_pred, target)
train_loss += loss.item()
# backward
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_loss = train_loss / len(train_dataloader)
# testing
test_loss, test_acc = 0, 0
model_v0.eval()
with torch.inference_mode():
for data, target in test_dataloader:
data, target = data.to(device), target.to(device)
y_pred = model_v0(data)
# hitung loss
loss = loss_fn(y_pred, target)
test_loss += loss.item()
# hitung akurasi
test_acc += accuracy(y_pred.cpu(), target.cpu())
test_loss = test_loss / len(test_dataloader)
test_acc = test_acc / len(test_dataloader)
print(
f"Epoch: {epoch + 1}/{EPOCHS} | Train Loss: {train_loss:.4f} | Test Loss: {test_loss:.4f} | Test Acc: {test_acc:.4f}"
)
train_end_time = timer()
total_train_time_model_v0 = get_print_time(train_start_time, train_end_time, device=str(next(model_v0.parameters()).device))
Epoch: 1/5 | Train Loss: 0.5929 | Test Loss: 0.5221 | Test Acc: 0.8138 Epoch: 2/5 | Train Loss: 0.4766 | Test Loss: 0.4905 | Test Acc: 0.8304 Epoch: 3/5 | Train Loss: 0.4556 | Test Loss: 0.4955 | Test Acc: 0.8282 Epoch: 4/5 | Train Loss: 0.4436 | Test Loss: 0.4628 | Test Acc: 0.8374 Epoch: 5/5 | Train Loss: 0.4366 | Test Loss: 0.4710 | Test Acc: 0.8360 Total waktu: 42.66 detik
Evaluasi Model¶
Berikutnya, kita akan mencoba untuk melakukan inferensi terhadap model yang telah dibuat dan menghitung nilai akurasi dari model tersebut
# single inference
model_v0.eval()
with torch.inference_mode():
data, target = next(iter(test_dataloader))
data, target = data.to(device), target.to(device)
y_pred = model_v0(data)
y_pred = torch.argmax(y_pred, dim=1)
# visualisasi 10 data pertama
plt.figure(figsize=(20, 6))
for i in range(10):
plt.subplot(2, 5, i + 1)
plt.imshow(data[i].cpu().squeeze(), cmap="gray")
plt.axis("off")
plt.title(f"Pred: {classes[y_pred[i]]} | True: {classes[target[i]]}")
# print metric
acc = accuracy(y_pred.cpu(), target.cpu())
print(f"Test Accuracy: {acc:.4f}")
Test Accuracy: 0.8438
dapat kita lihat bahwa model yang telah dibuat memiliki nilai akurasi sebesar 0.84, yang artinya model yang telah dibuat memiliki kemampuan yang cukup baik untuk melakukan klasifikasi gambar. Namun, kita akan mencoba untuk membuat model yang lebih baik dari model yang telah dibuat
Membuat Model V1¶
Selanjutnya, kita akan mencoba untuk membuat model yang lebih baik dari model yang telah dibuat. Model yang akan dibuat adalah model yang memiliki arsitektur yang sama dengan model yang telah dibuat, namun dengan penambahan nn.ReLU()
pada setiap layer yang telah dibuat.
class FashionMnistModel1(nn.Module):
def __init__(self, input_size, num_classes, hidden_size):
super().__init__()
self.layer_stack = nn.Sequential(
nn.Flatten(),
nn.Linear(input_size, hidden_size),
nn.ReLU(),
nn.Linear(hidden_size, num_classes),
nn.ReLU(),
)
def forward(self, x):
return self.layer_stack(x)
Menginstansiasi Model¶
model_v1 = FashionMnistModel1(input_size=28 * 28, num_classes=len(classes), hidden_size=10)
model_v1.to(device)
FashionMnistModel1( (layer_stack): Sequential( (0): Flatten(start_dim=1, end_dim=-1) (1): Linear(in_features=784, out_features=10, bias=True) (2): ReLU() (3): Linear(in_features=10, out_features=10, bias=True) (4): ReLU() ) )
Mengatur Loss, Optimizer, dan Accuracy¶
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model_v1.parameters(), lr=0.1)
accuracy = torchmetrics.Accuracy(task="multiclass", num_classes=len(classes))
Membuat Fungsi Train Loop¶
Agar lebih praktis dan memudahkan kita untuk melakukan training, kita akan membuat fungsi untuk melakukan training loop. Fungsi ini akan melakukan training loop, dan mengembalikan nilai loss dan akurasi dari model yang telah dibuat Pada dasarnya, fungsi ini sama dengan cara kita melakukan training pada model_v0.
def run_train(model, train_dataloader, test_dataloader, loss_fn, optimizer, accuracy, device, epochs=5):
train_start_time = timer()
for epoch in range(epochs):
# training
train_loss = 0
# enumerate dataloader
for batch_idx, (data, target) in enumerate(train_dataloader):
model.train()
data, target = data.to(device), target.to(device)
y_pred = model(data)
# hitung loss
loss = loss_fn(y_pred, target)
train_loss += loss.item()
# backward
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_loss = train_loss / len(train_dataloader)
# testing
test_loss, test_acc = 0, 0
model.eval()
with torch.inference_mode():
for data, target in test_dataloader:
data, target = data.to(device), target.to(device)
y_pred = model(data)
# hitung loss
loss = loss_fn(y_pred, target)
test_loss += loss.item()
# hitung akurasi
test_acc += accuracy(y_pred.cpu(), target.cpu())
test_loss = test_loss / len(test_dataloader)
test_acc = test_acc / len(test_dataloader)
# print
print(
f"Epoch: {epoch + 1}/{epochs} | Train Loss: {train_loss:.4f} | Test Loss: {test_loss:.4f} | Test Acc: {test_acc:.4f}"
)
train_end_time = timer()
total_train_time = get_print_time(train_start_time, train_end_time, device=str(next(model.parameters()).device))
print(f"Total waktu training: {total_train_time:.2f} detik")
Melakukan Training Model V1¶
# train
run_train(model_v1, train_dataloader, test_dataloader, loss_fn, optimizer, accuracy, device, epochs=5)
Epoch: 1/5 | Train Loss: 0.6597 | Test Loss: 0.7124 | Test Acc: 0.8009 Epoch: 2/5 | Train Loss: 0.6597 | Test Loss: 0.7124 | Test Acc: 0.8009 Epoch: 3/5 | Train Loss: 0.6597 | Test Loss: 0.7124 | Test Acc: 0.8009 Epoch: 4/5 | Train Loss: 0.6597 | Test Loss: 0.7124 | Test Acc: 0.8009 Epoch: 5/5 | Train Loss: 0.6597 | Test Loss: 0.7124 | Test Acc: 0.8009 Total waktu: 50.65 detik Total waktu training: 50.65 detik
Pembahasan:
Dengan menggunakan tambahan layer nn.ReLU()
, kita dapat melihat bahwa nilai akurasi dari model yang telah dibuat menjadi 0.8009 dari yang sebelumnya 0.8360. nn.ReLU()
tidak secara signifikan meningkatkan nilai akurasi dari model yang telah dibuat, namun dengan menggunakan nn.ReLU()
kita dapat menghindari terjadinya overfitting pada model yang telah dibuat.
Membuat Model V2¶
Selanjutnya kita akan membuat model yang lebih baik dari model yang telah dibuat. Pada model versi 2, kita akan menggunakan Convolutional Neural Network (CNN) untuk melakukan klasifikasi gambar. Sehingga, komposisi layer akan menjadi:
- Block 1:
nn.Conv2d
->nn.ReLU
->nn.Conv2d
->nn.ReLU
->nn.MaxPool2d
- Block 2:
nn.Conv2d
->nn.ReLU
->nn.Conv2d
->nn.ReLU
->nn.MaxPool2d
- Classifier:
nn.Flatten
->nn.Linear
Mari kita buat class ModelV2
untuk membuat model versi 2
class FashionMnistModel2(nn.Module):
def __init__(
self,
input_size,
hidden_size,
output_size,
):
super().__init__()
# BLOCK 1
self.block1 = nn.Sequential(
nn.Conv2d(in_channels=input_size, out_channels=hidden_size, kernel_size=3, stride=1, padding=1),
nn.ReLU(),
nn.Conv2d(in_channels=hidden_size, out_channels=hidden_size, kernel_size=3, stride=1, padding=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2),
)
# BLOCK 2
self.block2 = nn.Sequential(
nn.Conv2d(in_channels=hidden_size, out_channels=hidden_size, kernel_size=3, stride=1, padding=1),
nn.ReLU(),
nn.Conv2d(in_channels=hidden_size, out_channels=hidden_size, kernel_size=3, stride=1, padding=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2),
)
# BLOCK 3 (CLASSIFIER)
self.block3 = nn.Sequential(
nn.Flatten(),
nn.Linear(hidden_size * 7 * 7, output_size),
)
def forward(self, x):
x = self.block1(x)
x = self.block2(x)
x = self.block3(x)
return x
Instansiasi Model dan Mengatur Device¶
Setelah itu, seperti biasa, model yang telah diinstansiasi, dikirimkan kembali ke device dengan cara
model_v2.to(device)