Mengenal model MobileNet dan Uji Kinerjanya untuk Tugas Klasifikasi
MobileNetV1 merupakan salah satu arsitektur jaringan saraf konvolusional (CNN) yang dirancang khusus untuk tugas klasifikasi gambar di lingkungan perangkat bergerak dengan sumber daya terbatas. Berikut adalah beberapa keunggulan MobileNetV1 dalam implementasi PyTorch:
- Ringan dan Efisien:
- MobileNetV1 dirancang khusus untuk menjadi ringan dan efisien, sehingga sangat cocok untuk perangkat bergerak dengan daya komputasi terbatas seperti ponsel pintar.
- Menggunakan struktur Depthwise Separable Convolution untuk mengurangi jumlah parameter dan komputasi, sehingga memungkinkan penggunaan yang lebih efisien dari sumber daya perangkat keras.
- Depthwise Separable Convolution:
- MobileNetV1 memanfaatkan operasi konvolusi yang disebut Depthwise Separable Convolution. Ini terdiri dari dua tahap: konvolusi depthwise dan konvolusi titik.
- Depthwise Separable Convolution membantu mengurangi jumlah parameter dan komputasi, menghasilkan model yang lebih ringan tanpa mengorbankan kinerja.
- Sifat Scalability:
- MobileNetV1 memiliki sifat yang dapat diukur (scalable), yang memungkinkan pengguna untuk mengonfigurasi model sesuai dengan kebutuhan spesifik mereka.
- Ada parameter “width multiplier” yang memungkinkan Anda mengatur lebar (width) dari setiap layer, sehingga dapat disesuaikan dengan kebutuhan sumber daya perangkat keras.
- Pre-trained Model Tersedia:
- Model MobileNetV1 pre-trained sudah tersedia di dalam library PyTorch, memudahkan pengguna untuk menggunakannya untuk tugas transfer learning atau fine-tuning pada dataset khusus.
- Dukungan PyTorch:
- MobileNetV1 diimplementasikan dan dioptimalkan dengan baik dalam PyTorch, yang memudahkan penggunaan dan integrasi dengan ekosistem PyTorch.
- Dukungan PyTorch memungkinkan pengguna untuk dengan mudah memanfaatkan fitur-fitur PyTorch seperti autograd untuk pelatihan model.
MobileNetV2 adalah iterasi berikutnya dari MobileNetV1, dan dirancang untuk lebih meningkatkan kinerja dan efisiensi. Berikut adalah beberapa keunggulan MobileNetV2 dalam implementasi PyTorch:
- Inverted Residuals with Linear Bottlenecks:
- MobileNetV2 memperkenalkan konsep “Inverted Residuals” dengan “Linear Bottlenecks”. Ini membantu mengatasi masalah degradasi kinerja yang bisa terjadi pada model dengan depth (kedalaman) yang besar.
- Struktur ini membantu meningkatkan akurasi dan mempercepat pelatihan model.
- Linear Bottleneck:
- MobileNetV2 menggunakan blok Linear Bottleneck, yang memungkinkan untuk menjaga representasi informasi yang lebih baik melalui layer.
- Linear Bottleneck membantu menjaga informasi penting dan mengurangi kemungkinan kehilangan informasi selama proses pelatihan.
- Width Multiplier dan Resolutions:
- Seperti MobileNetV1, MobileNetV2 juga mendukung parameter “width multiplier” untuk mengatur lebar (width) dari setiap layer, memungkinkan skalabilitas untuk disesuaikan dengan kebutuhan.
- MobileNetV2 juga dapat diaplikasikan pada berbagai resolusi gambar, yang membuatnya lebih fleksibel untuk digunakan pada tugas dengan resolusi yang berbeda.
- Dengan dan Tanpa Ekspansi:
- MobileNetV2 menyediakan pilihan untuk menggunakan blok residual dengan atau tanpa ekspansi. Ekspansi di sini mengacu pada peningkatan dimensi fitur di dalam blok.
- Pilihan ini memberikan fleksibilitas lebih dalam mendesain arsitektur model tergantung pada kebutuhan dan ketersediaan sumber daya.
- Dukungan Depthwise Separable Convolution:
- Tetap memanfaatkan konsep Depthwise Separable Convolution, yang membantu mengurangi jumlah parameter dan komputasi, sehingga membuat model lebih ringan dan efisien.
- Dukungan PyTorch:
- Implementasi MobileNetV2 di PyTorch memastikan bahwa model ini dapat dengan mudah digunakan dan diintegrasikan dengan ekosistem PyTorch, termasuk dukungan untuk fitur-fitur seperti autograd.
mari kita coba langsung saja
import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms
lanjutnya persiapan dataset
Persiapan Data:
- Unduh dataset yang sesuai dengan tugas Anda, misalnya CIFAR-10 atau ImageNet.
- Terapkan transformasi yang sesuai, seperti normalisasi dan augmentasi
transform = transforms.Compose([ transforms.RandomResizedCrop(224), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) train_dataset = datasets.CIFAR10(root='./data', train=True, download=True, transform=transform) train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=4)
Bangun Model MobileNetV2:
- Gunakan modul
torchvision.models.mobilenet_v2
untuk mengambil model MobileNetV2. Jangan lupa mengganti jumlah kelas output sesuai dengan dataset Anda.
model = torch.hub.load('pytorch/vision:v0.10.0', 'mobilenet_v2', pretrained=False) model.classifier = nn.Linear(model.last_channel, num_classes)
Tentukan Kriteria dan Optimizer:
- Pilih kriteria (loss function) dan optimizer yang sesuai.
criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
Latih Model:
- Iterasi melalui dataset dan lakukan pelatihan.
num_epochs = 10 for epoch in range(num_epochs): model.train() for inputs, labels in train_loader: optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step()
Model Dari Sketch Awal
Contents
Sebenarnya kita bisa kok membuat Sketch Model Mobilenet V1 dari awal, kita bisa menggunakan model berikut
class MobileNetV1(nn.Module): def __init__(self, ch_in, n_classes): super(MobileNetV1, self).__init__() def conv_bn(inp, oup, stride): return nn.Sequential( nn.Conv2d(inp, oup, 3, stride, 1, bias=False), nn.BatchNorm2d(oup), nn.ReLU(inplace=True) ) def conv_dw(inp, oup, stride): return nn.Sequential( # dw nn.Conv2d(inp, inp, 3, stride, 1, groups=inp, bias=False), nn.BatchNorm2d(inp), nn.ReLU(inplace=True), # pw nn.Conv2d(inp, oup, 1, 1, 0, bias=False), nn.BatchNorm2d(oup), nn.ReLU(inplace=True), ) self.model = nn.Sequential( conv_bn(ch_in, 32, 2), conv_dw(32, 64, 1), conv_dw(64, 128, 2), conv_dw(128, 128, 1), conv_dw(128, 256, 2), conv_dw(256, 256, 1), conv_dw(256, 512, 2), conv_dw(512, 512, 1), conv_dw(512, 512, 1), conv_dw(512, 512, 1), conv_dw(512, 512, 1), conv_dw(512, 512, 1), conv_dw(512, 1024, 2), conv_dw(1024, 1024, 1), nn.AdaptiveAvgPool2d(1) ) self.fc = nn.Linear(1024, n_classes) def forward(self, x): x = self.model(x) x = x.view(-1, 1024) x = self.fc(x) return x
Untuk nilai channel dan ukuran gambar bisa kok kita sesuaikan, misalkan kita bisa menggunakan dataset MNIST yang punya ukuran 28*28 single channel.
Berikut kode lengkap yang saya gunakan, bisa kalian pelajari sendiri. Mengingat dataset nya cukup banyak, maka saya split saja biar kecil Cara Melakukan Split Dataset untuk Training Testing Validation sedangkan untuk. checkpoint nya bisa kalian gunakan Menyimpan Check Point pada Proses Iterasi Machine Learning
Kode berikut akan tampil jika kalian telah login/register
Berikut keterangan kode diatas
Menyiapkan dataset, dari hasil download beserta transform nya, karena ukurannya sudah pasti seragam, tidak perlu melakukan resize / croping image nya
Jangan lupa single channel = 1 dan jumlah kelas/target yaitu 10 kelas
transform = T.Compose([ #T.CenterCrop(224), T.ToTensor(), T.Normalize((0.1307,), (0.3081,))]) batch_size_train = 8 num_classes = 10 channel = 1 full_dataset = datasets.MNIST('data', train=True, download=False,transform = transform)
Spliting dataset biar nggak terlalu banyak
train_dataset,test_dataset, val_dataset = torch.utils.data.random_split(full_dataset, [0.5, 0.25, 0.25], generator=torch.Generator().manual_seed(42))
Buatkan loader training
train_dl = DataLoader(train_dataset,batch_size=batch_size_train, shuffle=True)
Kita panggil model dan optimizer serta jenis loss functionnya
inputs = inputs.to(device) targets = targets.to(device) torch.manual_seed(0) #biar nggak random lagi model = MobileNetV1(ch_in= channel, n_classes=num_classes) #out = model(torch.rand([8,1,28,28])) model = model.to(device) optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9) criterion = nn.CrossEntropyLoss() #karena menggunakan Linear
Untuk memastikan checkpoint sebelumnya apakah ada simpan? gunakan kode berikut, Jika True maka akan loading checkpoint sebelumnya
folder = 'checkpoint mobilenetv1 uji ke 2' epoch_current = 1 loss_list = list() if True: if os.path.exists(folder+'/model.pt'): print("menggunakan checkpoint") #loading checkpoint checkpoint = torch.load(folder+'/model.pt') #loading model model.load_state_dict(checkpoint['model_state_dict']) #loading optimizer optimizer.load_state_dict(checkpoint['optimizer_state_dict']) #loading epoch epoch_current = checkpoint['epoch'] #baca histori loss sebelumnya #loss_list = np.loadtxt(folder+'/loss.csv') loss_list = pd.read_csv(folder+"/loss.csv",names=['loss']) loss_list = loss_list['loss'].tolist() epoch_current = epoch_current+1 print("epoch_current ",epoch_current) else: print("tidak ada checkpoint") else: print("tidak menggunakan checkpoint")
Sesi pelatihan berikut ini ada 2 hal yang penting yaitu
- penyimpanan checkpoint dan
- break looping ketika ingin looping dihentikan secara manual
Kode berikut akan tampil jika kalian telah login/register
Lakukan evaluasi
#lakukan evaluasi model.eval() jumlah_benar = 0 jumlah_data = 0 with torch.no_grad(): for i,batch in enumerate(tqdm(train_dl)): images,labels = batch images = images.to(device) labels = labels.to(device) out = model(images) #prediksi = torch.argmax(out,dim=1) ## The output has unnormalized scores. To get probabilities, you can run a softmax on it. probabilities = torch.nn.functional.softmax(out, dim=0) prediksi = probabilities.argmax(dim=1) n = len(labels) benar = (prediksi==labels).sum() jumlah_data = jumlah_data + n jumlah_benar = jumlah_benar+benar acc = (jumlah_benar/jumlah_data)*100 print("\n akuarasi acc : ",np.round(acc.item(),decimals=2)," %")
Berikut hasil plotting loss tiap iterasi Mengenal model MobileNet dan Uji Kinerjanya untuk Tugas Klasifikasi
Saya menggunakan Backend Deep Learning dengan MPS GPU Apple M1 sehingga sangat cepat sekali untuk training nya hanya butuh 203 detik saja bila hanya menggunakan CPU akan butuh waktu sekitar 1000an detik.
Torch 2.1.2 CUDA None running dengan mps menggunakan checkpoint epoch_current 6 epoch: 11 final loss: 0.095: 1%| | 6/495 [20:16<27:39:05, 203.57s/it]
Jika dilihat sampai epoch ke 10 mengalami penurunan yang cukup bagus/smooth, kalian bisa menggunakan kode berikut
df = pd.read_csv('checkpoint mobilenetv1 uji ke 2/loss.csv',header=None,names=['loss']) #print(df.tail(5)) plt.figure() plt.plot(df['loss']) plt.xlabel('epoch') plt.ylabel('nilai Loss '+str(df.loc[len(df)-1]['loss'])) plt.title('Epoch Terakhir: '+str(len(df))) plt.show()
Akurasi yang sangat tinggi
Berikut ketika mencapai epoch ke 60 dengan loss dibawah 0.001
Saya akan coba dengan dataset training, testing dan validation dengan hasil sebagai berikut
dataset training
100%|██████████| 469/469 [00:11<00:00, 40.13it/s] akuarasi acc : 99.75 %
dataset testing
100%|██████████| 235/235 [00:05<00:00, 39.94it/s] akuarasi acc : 98.36 %
dataset validation
100%|██████████| 235/235 [00:05<00:00, 39.25it/s] akuarasi acc : 98.54 %
gimana? menarik sekali untuk digunakan
Referensi
MobileNetV1
MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications
paper: https://arxiv.org/abs/1704.04861
MobileNetV2
MobileNetV2: Inverted Residuals and Linear Bottlenecks
paper: https://arxiv.org/abs/1801.04381
MobileNetV3
Searching for MobileNetV3
paper: https://arxiv.org/abs/1905.02244
Model MobileNet V2
Bisa kalian menggunakan kode berikut untuk mobilenet V2
import torch.nn as nn from torchsummary import summary def dwise_conv(ch_in, stride=1): return ( nn.Sequential( #depthwise nn.Conv2d(ch_in, ch_in, kernel_size=3, padding=1, stride=stride, groups=ch_in, bias=False), nn.BatchNorm2d(ch_in), nn.ReLU6(inplace=True), ) ) def conv1x1(ch_in, ch_out): return ( nn.Sequential( nn.Conv2d(ch_in, ch_out, kernel_size=1, padding=0, stride=1, bias=False), nn.BatchNorm2d(ch_out), nn.ReLU6(inplace=True) ) ) def conv3x3(ch_in, ch_out, stride): return ( nn.Sequential( nn.Conv2d(ch_in, ch_out, kernel_size=3, padding=1, stride=stride, bias=False), nn.BatchNorm2d(ch_out), nn.ReLU6(inplace=True) ) ) class InvertedBlock(nn.Module): def __init__(self, ch_in, ch_out, expand_ratio, stride): super(InvertedBlock, self).__init__() self.stride = stride assert stride in [1,2] hidden_dim = ch_in * expand_ratio self.use_res_connect = self.stride==1 and ch_in==ch_out layers = [] if expand_ratio != 1: layers.append(conv1x1(ch_in, hidden_dim)) layers.extend([ #dw dwise_conv(hidden_dim, stride=stride), #pw conv1x1(hidden_dim, ch_out) ]) self.layers = nn.Sequential(*layers) def forward(self, x): if self.use_res_connect: return x + self.layers(x) else: return self.layers(x) class MobileNetV2(nn.Module): def __init__(self, ch_in=3, n_classes=1000): super(MobileNetV2, self).__init__() self.configs=[ # t, c, n, s [1, 16, 1, 1], [6, 24, 2, 2], [6, 32, 3, 2], [6, 64, 4, 2], [6, 96, 3, 1], [6, 160, 3, 2], [6, 320, 1, 1] ] self.stem_conv = conv3x3(ch_in, 32, stride=2) layers = [] input_channel = 32 for t, c, n, s in self.configs: for i in range(n): stride = s if i == 0 else 1 layers.append(InvertedBlock(ch_in=input_channel, ch_out=c, expand_ratio=t, stride=stride)) input_channel = c self.layers = nn.Sequential(*layers) self.last_conv = conv1x1(input_channel, 1280) self.classifier = nn.Sequential( nn.Dropout2d(0.2), nn.Linear(1280, n_classes) ) self.avg_pool = nn.AdaptiveAvgPool2d(1) def forward(self, x): x = self.stem_conv(x) x = self.layers(x) x = self.last_conv(x) x = self.avg_pool(x).view(-1, 1280) x = self.classifier(x) return x if __name__=="__main__": # model check model = MobileNetV2(ch_in=3, n_classes=1000) summary(model, (3, 224, 224), device='cpu')
Model CNN sederhana
Saya masih uji model diatas, dibandingkan dengan model CNN sederhana berikut
class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(1, 10, kernel_size=5) self.conv2 = nn.Conv2d(10, 20, kernel_size=5) self.conv2_drop = nn.Dropout2d() self.fc1 = nn.Linear(320, 50) self.fc2 = nn.Linear(50, 10) def forward(self, x): x = F.relu(F.max_pool2d(self.conv1(x), 2)) x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2)) x = x.view(-1, 320) x = F.relu(self.fc1(x)) x = F.dropout(x, training=self.training) x = self.fc2(x) return F.log_softmax(x)
yang menggunakan pengaturan sebagai berikut referensi: https://nextjournal.com/gkoehler/pytorch-mnist
network = Net() optimizer = optim.SGD(network.parameters(), lr=learning_rate, momentum=momentum) def train(epoch): network.train() for batch_idx, (data, target) in enumerate(train_loader): optimizer.zero_grad() output = network(data) loss = F.nll_loss(output, target) loss.backward() optimizer.step() if batch_idx % log_interval == 0: print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format( epoch, batch_idx * len(data), len(train_loader.dataset), 100. * batch_idx / len(train_loader), loss.item())) train_losses.append(loss.item()) train_counter.append( (batch_idx*64) + ((epoch-1)*len(train_loader.dataset))) torch.save(network.state_dict(), '/results/model.pth') torch.save(optimizer.state_dict(), '/results/optimizer.pth')