Mengenal model MobileNet dan Uji Kinerjanya untuk Tugas Klasifikasi

By | January 21, 2024
Print Friendly, PDF & Email
825 Views

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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. Dukungan Depthwise Separable Convolution:
    • Tetap memanfaatkan konsep Depthwise Separable Convolution, yang membantu mengurangi jumlah parameter dan komputasi, sehingga membuat model lebih ringan dan efisien.
  6. 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.
See also  #Video Tutorial - Belajar Python Bagian 1

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

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

See also  Pytorch memperkenalkan TorchRec, library open source untuk Sistem Rekomendasi

Kode berikut akan tampil jika kalian telah login/register

Existing Users Log In




Enter Captcha Here :

   

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

Existing Users Log In




Enter Captcha Here :

   

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

See also  Computer Vision Annotation Tool

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')

 

 

 

Leave a Reply