Kompresi Mask Image untuk Mengurangi Penggunaan Loading Dataset

By | May 5, 2025
274 Views

Kompresi Mask Image untuk Mengurangi Penggunaan Loading Dataset –  Pelatihan deep learning membutuhkan banyak sekali dataset dan proses iterasi yang banyak, oleh sebab itu penggunaan sumber daya haruslah efisien. Data yang sifat nya berulang bisa kita siasati dengan teknik kompresi.

Mask Image pada dataset instance segmentation merupakan salah satunya karena didalam image tersebut hanya terdiri dari angka 1 dan 0 bila menggunakan tipe data boolean atau bahkan bernilai 0 dan 255 dengan tipe data unsigned integer 8 (uint8) atau 8 bit.

Kalian bisa membayangkan dengan 1 target data bahkan bisa terdiri dari beberapa label/class dengan ukuran yang cukup besar akan banyak menghabiskan resource RAM sehingga mengurangi jumlah ukuran batch nantinya.

Berdasarkan pengalaman saya ketika tidak menggunakan kompresi data, kebutuhannya sangat besar sekali bisa mencapai 32 GB itupun sudah megap-megap walaupun kita bisa menggunakan SWAP memory namun saya sangat menghindari tersebut.

Sebagai gambaran saja, saya akan menampilkan gambar inputs dan target yang fokus pada instance segmentation.

masing-masing walaupun beda isi pixel akan sama isi byte nya

import numpy as np
import cv2
from matplotlib import pyplot as plt


person = cv2.imread('1.jpg',0)
segmen = cv2.imread('1.png',0)
plt.figure()
plt.subplot(1,2,1), plt.imshow(person,cmap='gray'), plt.title('inputs')
plt.subplot(1,2,2), plt.imshow(segmen,cmap='gray'), plt.title('targets')
plt.show()

from pympler import asizeof
size_person = asizeof.asizeof(person)
size_segmen = asizeof.asizeof(segmen)
print(f"Ukuran data di memori: {size_person} bytes")
print(f"Ukuran data di memori: {size_segmen} bytes")

hasilnya ukuran ke duanya yaitu

Ukuran data di memori: 50656 bytes
Ukuran data di memori: 50656 bytes

Sekilas mengenai RLE

Run-Length Encoding (RLE) adalah teknik kompresi data sederhana yang bekerja dengan cara menggantikan urutan elemen yang berulang dengan pasangan nilai dan jumlah pengulangan (run). Teknik ini sangat efektif untuk data yang memiliki banyak elemen yang berulang secara berurutan, seperti citra biner atau bitmap.

See also  Cara menangani dataset yang besar

Cara Kerja RLE

RLE mencari “run” — yaitu, urutan karakter/data yang sama berulang — dan menggantinya dengan:

jumlah_kemunculan + nilai

Contoh Sederhana:

Misalkan ada data:

AAAAABBBCCDAA

RLE akan mengubahnya menjadi:

5A3B2C1D2A

Penjelasan:

  • A muncul 5 kali → 5A

  • B muncul 3 kali → 3B

  • C muncul 2 kali → 2C

  • D muncul 1 kali → 1D

  • A muncul 2 kali lagi → 2A

Saya akan membuatkan class Kompresi untuk array2D dengan output array atau list, kalian bisa menggunakan salah satunya.

class Kompresi:
    def __init__(self):
        pass

    def rle_encode_2d(self,arr2d):
        """Kompresi RLE untuk array 2D."""
        flat = arr2d.flatten()
        values, lengths = self.__rle_encode__(flat)
        shape = arr2d.shape
        return values, lengths, shape
        
    def rle_encode_list(self,arr2d):
        """Kompresi RLE untuk array 2D."""
        flat = arr2d.flatten()
        values, lengths = self.__rle_encode__(flat)
        shape = arr2d.shape
        return {'values':values, 'lengths':lengths, 'shape':shape}
    
    def rle_decode_2d(self,values, lengths, shape):
        """Dekompresi RLE untuk array 2D."""
        flat = self.__rle_decode__(values, lengths)
        return flat.reshape(shape)
        
    def rle_decode_list(self,data):
        values = data['values']
        lengths = data['lengths']
        shape = data['shape']
        return self.rle_decode_2d(values, lengths, shape)
    
    # Gunakan fungsi rle_encode dan rle_decode dari sebelumnya
    def __rle_encode__(self,arr):
        if len(arr) == 0:
            return np.array([]), np.array([])
        
        diff = np.diff(arr)
        run_ends = np.where(diff != 0)[0] + 1
        run_starts = np.insert(run_ends, 0, 0)
        run_ends = np.append(run_ends, len(arr))
        
        values = arr[run_starts]
        lengths = run_ends - run_starts
        return values, lengths
    
    def __rle_decode__(self,values, lengths):
        return np.repeat(values, lengths)

kita fokus pada gambar binary/segmen diatas

kompresi = Kompresi()
segmen_kompres = kompresi.rle_encode_list(segmen)
size_segmen_kompres = asizeof.asizeof(segmen_kompres)
print(f"Ukuran data di memori: {size_segmen_kompres} bytes")

hasilnya

Ukuran data di memori: 3976 bytes

sehingga ukuranya hanya 7% dari semula!

sedangkan untuk extract / un compress kalian bisa gunakan fungsi berikut

segmen_unkompres = kompresi.rle_decode_list(segmen_kompres)
plt.figure()
plt.imshow(segmen_unkompres,cmap='gray')
plt.show()

Bagaimana dengan gambar grayscale!

hemm RLE sebenarnya sangat cocok dengan content yang berulang! tapi mari kita coba saja

person_kompres = kompresi.rle_encode_list(person)
size_person_kompres = asizeof.asizeof(person_kompres)
print(f"Ukuran data di memori: {size_person_kompres} bytes")

hasilnya malah akan lebih besar!

Ukuran data di memori: 361344 bytes

Kompresi Mask Image untuk Mengurangi Penggunaan Loading Dataset sangat cocok menggunakan kompresi RLE yang fokus mengurangi data yang berulang!

See also  Linear Regression dengan Konsep Gradient Descent

Jadi saat ini saya sudah banyak sekali menggunakan kompresi RLE pada pipeline deep learning untuk mengurangi kebutuhan RAM yang sangat besar.

Sebagai bonus diakhir artikel, sudah saya siapkan class Kompresi khusus untuk tensor pytorch

import torch

class KompresiTensor:
    def __init__(self):
        pass

    def rle_encode_2d(self, tensor2d: torch.Tensor):
        """Kompresi RLE untuk tensor 2D PyTorch."""
        flat = tensor2d.flatten()
        values, lengths = self.__rle_encode__(flat)
        shape = tensor2d.shape
        return values, lengths, shape

    def rle_encode_list(self, tensor2d: torch.Tensor):
        """Kompresi RLE untuk tensor 2D PyTorch dalam format dict."""
        flat = tensor2d.flatten()
        values, lengths = self.__rle_encode__(flat)
        shape = tensor2d.shape
        return {'values': values, 'lengths': lengths, 'shape': shape}
    
    def rle_decode_2d(self, values: torch.Tensor, lengths: torch.Tensor, shape: torch.Size):
        """Dekompresi RLE untuk tensor 2D PyTorch."""
        flat = self.__rle_decode__(values, lengths)
        return flat.view(shape)
    
    def rle_decode_list(self, data: dict):
        """Dekompresi dari format dict."""
        values = data['values']
        lengths = data['lengths']
        shape = data['shape']
        return self.rle_decode_2d(values, lengths, shape)

    def __rle_encode__(self, arr: torch.Tensor):
        """Private: Kompresi 1D tensor dengan RLE."""
        if arr.numel() == 0:
            return torch.tensor([]), torch.tensor([])

        diff = arr[1:] != arr[:-1]
        run_ends = torch.nonzero(diff).flatten() + 1
        run_starts = torch.cat((torch.tensor([0], device=arr.device), run_ends))
        run_ends = torch.cat((run_ends, torch.tensor([arr.numel()], device=arr.device)))

        values = arr[run_starts]
        lengths = run_ends - run_starts
        return values, lengths

    def __rle_decode__(self, values: torch.Tensor, lengths: torch.Tensor):
        """Private: Dekompresi 1D tensor dengan RLE."""
        return torch.repeat_interleave(values, lengths)