
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.
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!
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)