Klasifikasi Gambar Menggunakan Deeplearning Untuk Kasus Sel Darah Putih 🩸 [Part 1]

Ramdhina Finita
12 min readFeb 15, 2023

--

Haa?? sel darah putih bisa kita deteksi secara cepat dengan deeplearning?

Mungkin sebagaian orang bertanya-tanya, apakah semakin berkembangnya zaman.. langkah-langkah tradisional dalam dunia medis bisa juga ikut berkembang seiring berkembangnya teknologi?

YAAAA… Pastiii!! karena saat ini dalam dunia medis sedang berbondong-bondong untuk transformasi dibidang teknologi juga.. Dipicu dalam kasus Covid-19 sehingga dituntut untuk kerja cepat dan dalam deteksi virus pun harus cepat.

Salah satu hal yang dapat dilakukan dalam dunia medis yakni melakukan klasifikasi sel darah. Prosedur tradisional klasifikasi sel darah menggunakan mikroskop di laboratorium hematologi dilakukan untuk memperoleh informasi jenis sel darah. Telah menjadi landasan di laboratorium hematologi untuk mendiagnosis dan memantau gangguan hematologi sebagai langkah pencegahan penyakit secara dini. Namun, prosedur manual melalui serangkaian uji laboratorium dapat memakan waktu cukup lama. Oleh karena itu penelitian ini ditujukan khusus untuk dapat membantu dalam proses tahap awal klasifikasi jenis sel darah putih secara otomatis di bidang medis.

Upaya untuk mengatasi lamanya waktu dan untuk keperluan diagnosis awal dapat menggunakan teknik pengolahan citra berdasarkan citra dari sel darah tersebut menggunakan Deep Learning.

Deep Learning menggunakan Convolution Neural Networks (CNN), saat ini merupakan pilihan terbaik dalam aplikasi pencitraan medis seperti deteksi dan klasifikasi. Namun, seperti yang kita ketahui penggunaan CNN dapat mencapai hasil yang maksimal jika memiliki kumpulan data yang besar untuk dilatih. Dalam banyak kasus, kumpulan data pasti terbatas dan mungkin tidak cukup untuk melatih CNN dari awal. Dalam skenario seperti itu, untuk memanfaatkan kekuatan CNN dan pada saat yang sama dapat mengurangi sumber daya komputasi, sehingga mmetode transfer learning dapat digunakan. Beberapa metode yang akan kita gunakan pada kasus ini yakni VGG19, Resnet50, dan MobileNetV2.

WBC image classification and generative models based on convolutional neural network

Transfer learning sendiri merupakan metode yang menggunakan jaringan yang sudah dilatih sebelumnya dan menggunakannya sebagai titik awal untuk mempelajari tugas baru. Melakukan fine tuning network dengan transfer learning jauh lebih cepat dan lebih mudah daripada melatih network dari awal dengan bobot yang diinisialisasi secara acak.

Tahapan utama yang kita lakukan yakni:

  • Pre-Proses Data dan Eksplorasi Data
  • Build Model
  • Evaluasi Model

Pada tahap ini kita mulai untuk penggunaan VGG19 yakni model pertama yang kita coba untuk buat dalam klasifikasi ini.

Pre-Proses Data dan Eksplorasi Data

Data yang kita gunakan yakni berasal dari kumpulan 12.500 image sel darah yang sudah diperbesar dalam format (JPEG). Kelas sel darah yang ingin kita gunakan yakni 4 jenis yakni Eosinofil, Limfosit,Monosit, dan Neutrofil. Jenis sel Basofil tidak diikut sertakan karena keterbatasan data, yang mana Basofil sendiri jarang ditemukan pada darah orang normal.

Import Library/Packages

import os

# Neural Network Model
import tensorflow as tf
import keras
import keras.applications as A
import keras.backend as B
from keras import regularizers
from keras.models import Sequential,Model
from keras.layers import Activation
from keras.layers.core import Dense,Flatten,Dropout
from keras.metrics import categorical_crossentropy
from keras.preprocessing.image import ImageDataGenerator
from keras.layers.convolutional import *

from tensorflow.keras.layers import BatchNormalization, Lambda
from tensorflow.keras.models import load_model
from tensorflow.keras.optimizers import Adam, RMSprop , SGD
from tensorflow.keras.applications import ResNet50
from tensorflow.keras import layers , Sequential
from tensorflow.keras.preprocessing.image import ImageDataGenerator, array_to_img, img_to_array, load_img

# Model Tuner
from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras.layers import Dense, GlobalAveragePooling2D, Input, GlobalMaxPool2D
from tensorflow.keras import Model
from keras_tuner.tuners import RandomSearch

# Visualisasi Confussion Matrix
from sklearn.metrics import classification_report, confusion_matrix, accuracy_score
import numpy as np
import pandas as pd
from shutil import copyfile
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
import plotly
import numpy as np

Hal pertama yang harus kita lakukan adalah menguraikan data sebelum membangun model. Dalam masalah klasifikasi gambar, merupakan praktik umum untuk meletakkan setiap gambar pada folder terpisah berdasarkan kelas/label target. Misalnya, di folder train di data yang kita miliki, kita memiliki 2 folder utama yakni Train dan Test. Dimana kita perlu membaca datanya terlebih dahulu dan melakukan pembagian data train.

Pada tahap ini saya mencoba untuk membagi menjadi 80% Training, 20% validasi dari path TRAIN, dan TEST tidak dibagi lagi karena sudah ada path nya sendiri.

Data Training, seperti namanya, data yang digunakan untuk training model.

Data Validation, digunakan untuk proses validasi model dan mencegah overfitting.

Dataset di input ke sistem untuk menghitung loss function, tanpa melakukan update bias dan weight.

Setiap epoch atau iterasi, proses training dan validasi dilakukan beruntutan. Setiap selesai melakukan training, dilanjutkan dengan proses validasi.

Jika nilai loss function dari proses validasi naik, training model dihentikan.

Data Testing, digunakan untuk testing model, sebagai simulasi penggunaan model pada dunia nyata. Data testing tidak boleh pernah dilihat oleh model sebelumnya.

# Define the directories for the train, and test sets from folder input
train_path = '../input/blood-cells/dataset2-master/dataset2-master/images/TRAIN'
test_path = '../input/blood-cells/dataset2-master/dataset2-master/images/TEST'

Cek Dimensi Gambar

from PIL import Image

train_path1 = '../input/blood-cells/dataset2-master/dataset2-master/images/TRAIN/EOSINOPHIL'
test_path1 = '../input/blood-cells/dataset2-master/dataset2-master/images/TEST/EOSINOPHIL'
for file_name in os.listdir(train_path1):
file_path = os.path.join(train_path1, file_name)
if os.path.isfile(file_path):
img = Image.open(file_path)
width, height = img.size
# print("width:", width)
# print("Height:", height)
for file_name in os.listdir(test_path1):
file_path = os.path.join(test_path1, file_name)
if os.path.isfile(file_path):
img = Image.open(file_path)
width, height = img.size
# print("width:", width)
# print("Height:", height)
# Output (width: 320, Height: 240)

Berdasarkan hasil pengencekan dimensi gambar, semua gambar pada salah satu contoh label darah putih memiliki lebar 320 piksel dan tinggi 240 piksel. Sehingga perlu dilakukan proses rescale dengan ukuran yang sesuai dengan model yang digunakan.

VGG19

Terdapat beberapa part yang harus kita lalui sebelum kita membuat sebuah model machine leaning. Yakni, data augmentasi yang merupakan tekni untuk meningkatan keragaman dataset train untuk mencegah terjadinya overfitting.

Jika kumpulan data besar dan beragam, augmentasi gambar mungkin tidak diperlukan. Namun, jika kumpulan datanya kecil atau tidak cukup beragam, augmentasi gambar dapat digunakan untuk memperbesar ukuran kumpulan data secara artifisial dan mengekspos model ke rentang variasi yang lebih luas.

Selain itu, saat mengerjakan project klasifikasi gambar, jika kumpulan data yang kita miliki tidak seimbang, dimana beberapa kelas kurang terwakili, augmentasi gambar dapat digunakan untuk membuat lebih banyak sampel untuk kelas yang kurang terwakili.

Penting untuk diperhatikan bahwa, Kita sebaiknya hanya menerapkan augmentasi data pada set data train, bukan pada set data test dan validasi. Karena kita pada dasarnya ingin model kita dapat digeneralisasikan ke data baru yang tidak terlihat, jadi sebaiknya kita tidak menggunakan teknik augmentasi data yang sama pada set validasi dan pengujian.

Augmentasi Data

Melakukan pembagian data menjadi 3 dataset dengan membaca terlebih dahulu datanya menggunakan keras ImageDataGenerator dan melakukan splitting:

# Define the data generators
train_datagen = ImageDataGenerator(rescale=1./255,
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
horizontal_flip=True,
fill_mode='nearest',
validation_split=0.2)

# validation_datagen = ImageDataGenerator(rescale=1./255 )

test_datagen = ImageDataGenerator(rescale=1./255 )

# Change the batchsize according to your system RAM
train_batchsize = 32
val_batchsize = 32
test_batchsize = 32

rescale: adalah nilai dimana kita akan melipatgandakan data sebelum diproses lainnya. Gambar asli kami terdiri dari koefisien RGB pada 0–255, tetapi nilai tersebut akan terlalu tinggi untuk diproses oleh model kami (mengingat tingkat pembelajaran tipikal), jadi kami menargetkan nilai antara 0 dan 1 sebagai gantinya dengan menskalakan dengan 1/255. faktor.

rotation_range: adalah nilai dalam derajat (0–180), rentang untuk memutar gambar secara acak

width_shift dan height_shift: adalah rentang (sebagai bagian dari total lebar atau tinggi) untuk menerjemahkan gambar secara acak secara vertikal atau horizontal

shear_range: adalah untuk menerapkan transformasi geser secara acak

zoom_range: adalah untuk memperbesar gambar secara acak

horizontal_flip: adalah untuk membalikkan separuh gambar secara acak secara horizontal — relevan bila tidak ada asumsi asimetri horizontal (mis. gambar dunia nyata).

fill_mode: adalah strategi yang digunakan untuk mengisi piksel yang baru dibuat, yang dapat muncul setelah rotasi atau pergeseran lebar/tinggi.

Langkah selanjutnya kita perlu menghasilkan kumpulan data gambar dari direktori/path dengan metode flow_from_directory

# Use the flow_from_directory method to generate batches of image data
train_generator = train_datagen.flow_from_directory(
train_path,
target_size=(224, 224),#224
batch_size=train_batchsize,
class_mode='categorical',
seed=42,
subset = "training")

validation_generator = train_datagen.flow_from_directory(
train_path,
target_size=(224, 224),
batch_size=val_batchsize,
class_mode='categorical',
shuffle=False,
seed=42,
subset='validation') #this way the images of the validation set are not augmented

test_generator = test_datagen.flow_from_directory(
test_path,
shuffle=False, #which means that the images are not augmented
batch_size=test_batchsize,
class_mode='categorical',
seed=42,
target_size=(224, 224))
Found 7968 images belonging to 4 classes.
Found 1989 images belonging to 4 classes.
Found 2487 images belonging to 4 classes.

Setelalh kita lakukan proses data augmentasi dan rescale gambar, hasil dimensi gambar train dan test yang kita miliki sekarang yakni 224 x 224 piksel dengan warna RGB.

Perlu diperhatikan, penggunaan model VGG19 ini sangat sensitif terhadap ukuran data image yang mana model ini sangat baik digunakan pada data gambar yang pikselnya 224 x 224 atau mendekatinya.

# Cek kelas 
class_test_labels = test_generator.class_indices
class_train_labels = train_generator.class_indices
class_val_labels = validation_generator.class_indices
print(class_test_labels)
print(class_train_labels)
print(class_val_labels)
{'EOSINOPHIL': 0, 'LYMPHOCYTE': 1, 'MONOCYTE': 2, 'NEUTROPHIL': 3}
{'EOSINOPHIL': 0, 'LYMPHOCYTE': 1, 'MONOCYTE': 2, 'NEUTROPHIL': 3}
{'EOSINOPHIL': 0, 'LYMPHOCYTE': 1, 'MONOCYTE': 2, 'NEUTROPHIL': 3}

Visualisasi Data Gambar

Setelah berhasil memuat dan menerapkan augmentasi data saat itu juga sesuai dengan parameter yang ditentukan. Sekarang, di bagian ini, tahap memvisualisasikan gambar untuk memastikan gambar dimuat dengan benar.

# Visualiasi data Train
plt.figure(figsize=(9,9))
for i in range(9):
# 3x3 grid
plt.subplot(330 + 1 + i)
batch = train_generator.next()[0]
image = batch[0]
plt.imshow(image)
plt.show();

Cek Visualisasi Proposi Dataset

class_names = ['EOSINOPHIL', 'LYMPHOCYTE', 'MONOCYTE', 'NEUTROPHIL']

_, train_counts = np.unique(train_generator.classes, return_counts = True)
_, val_counts = np.unique(validation_generator.classes, return_counts = True)
_, test_counts = np.unique(test_generator.classes, return_counts = True)

pd.DataFrame({'train': train_counts, "val": val_counts, "test": test_counts}, index = class_names).plot.bar()

plt.show()
pd.DataFrame({'Train': train_counts, 
"Val": val_counts,
"Test": test_counts},
index = class_names)
plt.pie(train_counts,
explode=(0, 0, 0, 0) ,
labels=class_names,
autopct='%1.1f%%')
plt.axis('equal')
plt.title('Proporsi setiap kelas observasi - Train')
plt.show()

Berdasarkan visuaslisasi dari 3 dataset yang kita miliki, data semua balance atau seimbang artinya tidak perlu untuk melakukan tahapan balancing dataset lagi

Build Model VGG19

vgg19 = tf.keras.applications.VGG19(include_top=False, 
weights='imagenet',
input_tensor=None,
input_shape=(224, 224, 3),
pooling = 'max',
classes=4,
classifier_activation='softmax')
print(vgg19.summary()) #membaca summary model

Kode diatas merupakan tahapan pembuatan model menggunakan VGG19 dari modul aplikasi TensorFlow Keras. Model VGG19 ini meurpakan salah satu model CNN untuk klasifikasi gambar. Parameter yang digunakan pada kode diatas yakni:

include_top = False artinya lapisan yang terhubung sepenuhnya di bagian atas model, yang digunakan untuk klasifikas tidak akan diikutsertakan.

weights = imagenet menetapkan bahwa model harus diinisialisasi dengan bobot yang telah dilatih sebelumnya dari kumpulan data ImageNet.

input_tensor = None berarti tidak ada tensor input yang akan diteruskan ke model.

input_shape = (224, 224, 3) mengatur bentuk gambar input menjadi 224x224 piksel dan 3 saluran warna (RGB) Red, Green, Blue atau berwarna.

pooling = max menentukan bahwa max pooling harus digunakan dalam model. Dimana, max pooling ini merupakan salah satu teknik untuk downsample dimensi dari iamge pada CNN sambil mempertahankan fitur-fitur penting didalamnya.

classes = 4 menetapkan bahwa model harus dilatih untuk mengklasifikasikan gambar ke dalam 4 kelas.

classifier_activation = softmax menetapkan bahwa fungsi aktivasi softmax harus digunakan pada lapisan klasifikasi akhir model.

Kode dibawah berguna untuk membuat model (VGG19) menggunakan fungsi dari Sequential API dari Keras yang mana memungkinkan kita menambah layer satu persatu kemudian memodifikasi layer dari model VGG19.

Kode looping dibawah dibuat sebagai cara untuk menyempurnakan model VGG19 yang telah dilatih sebelumnya pada kumpulan data baru. Dengan membekukan lapisan bawah model, proses pelatihan hanya akan memperbarui bobot dari 4 lapisan terakhir, yang memungkinkan model beradaptasi dengan data baru sambil tetap memanfaatkan bobot yang telah dilatih sebelumnya di lapisan bawah.

model = Sequential()

# looping pertama
for layer in vgg19.layers[:-3]: #melakukan subset dari layer kecuali 3 terakhir agar tidak dilakukan pelatihan. Artinya lapisan yang dilakukan pelatihan hanya 19 lapisan dari atas yang tetap dapat dilatih.
layer.trainable=False

#looping kedua
for layer in vgg19.layers:
print(layer,layer.trainable)
# Add the vgg convolutional base model
model.add(vgg19)

# Add new layers
model.add(Flatten())
model.add(BatchNormalization())
model.add(Dense(32, activation='relu' ))
model.add(Dropout(0.2))
model.add(BatchNormalization())
model.add(Dense(16, activation='relu' ))
model.add(Dropout(0.2))
model.add(BatchNormalization())
model.add(Dense(8, activation='relu' ))
model.add(Dropout(0.2))
model.add(BatchNormalization())
model.add(Dense(4, activation='softmax'))
model.summary()

Kode diatas merupakan tahapan dalam membuat model menggunakan CNN dengan arsitektur VGG19. Model diatas dutambah beberapa lapisan, dimana lapisan pertama dengan `model.add(vgg19)` dimana memanggil model VGG19 yang sebelumnya sudah di run dan dilatih sebelumnya sebagai titik awal training model.

Setelah itu, kita menambahkan beberapa layer baru kedalam model. Rincian pada setiap layer:

* `Flatten()`: Lapisan ini berguna untuk meratakan output dari lapisan sebelumnya, yang diperlukan untuk menyiapkan data untuk lapisan yang terhubung sepenuhnya.

* `BatchNormalization()`: Lapisan ini berguna untuk menormalkan output dari lapisan sebelumnya, yang dapat membantu meningkatkan kinerja model dengan mengurangi pergeseran kovariat internal.

* `Dense(32, activation=’relu’ )`: Lapisan ini adalah lapisan yang terhubung sepenuhnya dengan 32 neuron dan fungsi aktivasi unit linier yang diperbaiki dengan (ReLU). Fungsi aktivasi ReLU digunakan untuk memperkenalkan non-linearitas dalam model.

* `Dropout(0.2)`: Lapisan ini digunakan untuk mencegah overfitting dengan menonaktifkan secara acak 20% neuron di lapisan sebelumnya selama pelatihan.

Lapisan di atas diulang beberapa kali, dengan jumlah neuron di lapisan padat berkurang setiap kali, dan lapisan terakhir adalah lapisan **`Dense(4, activation=’softmax’)`** yang terhubung sepenuhnya dengan 4 neuron dan fungsi aktivasi softmax. Fungsi softmax digunakan untuk klasifikasi multi-kelas, di mana ia menghitung probabilitas untuk setiap kelas.

Terakhir, **`model.summary()`** fungsi tersebut digunakan untuk mencetak ringkasan arsitektur model, termasuk jumlah parameter untuk setiap lapisan.

from random import seed
seed(123)
model.compile(loss='categorical_crossentropy',
optimizer=Adam(learning_rate=0.001),
metrics=['accuracy'])

Kode diatas digunakan untuk mengkompilasi model vgg19. Pengaturan ini diperlukan agar model dapat melatih dan mengevaluasi kinerjanya.

1. Fungsi pertama yakni `loss` -> berfungsi untuk menentukan fungsi kerugian yang digunakan selama proses training. Dalam hal ini, fungsi loss yang digunakan adalah `categorical_crossentropy`, yang merupakan fungsi loss yang umum digunakan untuk klasifikasi multi-kelas. Fungsi loss ini mengukur perbedaan antara probabilitas kelas yang diprediksi dan label kelas yang sebenarnya.

2. Fungsi kedua yakni `optimizer` -> berfungsi untuk menentukan algoritma optimal yang akan digunakan untuk memperbaharui bobot model selama proses training. Dalam hal ini, algoritma yang digunakan uakni `Adam` dengan learning rate = 0,001. Adam ini merupakan algoritma yang paling populer yang mana merupakan gabungan fari algoritma AdaGrad dan RMSProp.

3. Fungsi ketiga yakni `metrics` -> berfungsi untuk menentukan metrik apa yang akan digunakan dalam proses evaluasi model selama proses training dan testing. Dalam hal ini, metrik yang digunakan yakni `accuracy` yang merupakan proporsi insttance yang diklasifikasikan dengan benar.

# Variabel history akan menyimpan riwayat pelatihan model, yang nantinya dapat digunakan untuk memvisualisasikan kinerja model selama training.
history0 = model.fit(train_generator, #turunan dari ImageDataGenerator yang berisi data gambar untuk dataset train
epochs=50,#menentukan berapa kali model akan mengulang seluruh dataset train.
callbacks=[tf.keras.callbacks.EarlyStopping( #memonitor validasi loss dan menghentikan proses pelatihan jika validasi loss tidak membaik selama 3 epoch. Ini juga mengembalikan bobot model terbaik.
monitor='val_loss',
patience=3,
restore_best_weights=True)],#digunakan untuk menentukan daftar callback yang akan dipanggil pada titik berbeda selama training
validation_data=validation_generator, #digunakan untuk menentukan set validasi yang akan digunakan untuk mengevaluasi performa model selama training.
verbose=1) #digunakan untuk menentukan tingkat detail yang akan dicetak ke konsol selama pelatihan
# save model VGG19
model.save("/kaggle/working/model_vgg19_0.h5")

Evaluasi Model

#Menampilkan plot dari menggunakan callbacks

accuracy = history0.history['accuracy']
val_acc = history0.history['val_accuracy']
loss = history0.history['loss']
val_loss = history0.history['val_loss']

epochs = range(len(accuracy))

plt.plot(epochs, accuracy, 'b', label='Training accuracy')
plt.plot(epochs, val_acc, 'r', label='Validation accuracy')
plt.title('Training and validation accuracy')
plt.legend()

plt.figure()

plt.plot(epochs, loss, 'b', label='Training loss')
plt.plot(epochs, val_loss, 'r', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()

plt.show()
# Confussion Matrix dengan data test pada model terbaik yakni model0
def evaluate_model(model0, test_generator):
evaluate = model0.evaluate(test_generator)
print('Loss of the model is - ', evaluate[0])
print('Accuracy of the model is - ', evaluate[1]*100, '%')

classes = list(test_generator.class_indices.keys())
Y_pred = model.predict(test_generator)
y_pred = np.argmax(Y_pred, axis=1)

print(classification_report(test_generator.classes, y_pred, target_names=classes))
print('Confusion Matrix')


# acc = accuracy_score(validation_generator.labels, predictions)
# cm = tf.math.confusion_matrix(validation_generator.labels, predictions)
# clr = classification_report(validation_generator.labels, predictions, target_names=CLASS_NAMES)

conf_matrix = confusion_matrix(test_generator.classes, y_pred)
df_cm = pd.DataFrame(conf_matrix, index = classes, columns = classes)
ax = sns.heatmap(df_cm, annot=True, fmt='g', vmin=0, cmap='Reds', cbar=True)

ax.set_title('Seaborn Confusion Matrix with labels\n\n');
ax.set_xlabel('\nPredicted Values')
ax.set_ylabel('Actual Values ');

## Ticket labels - List must be in alphabetical order

evaluate_model(model0, test_generator)
# Model VGG19
print("The best Training accuracy = {:.3f}%".format(max(accuracy)*100))
print("The best validation accuracy = {:.3f}% ".format(max(val_acc)*100))
print("The best Test accuracy =",(model0.evaluate(test_generator)[1]*100))
The best Training accuracy = 89.734%
The best validation accuracy = 94.369%
78/78 [==============================] - 13s 159ms/step - loss: 0.8577 - accuracy: 0.7427
The best Test accuracy = 74.26618337631226

Hasil image classification menggunakan model VGG19 pada data sel darah putih diatas menghasilkan accuracy sebesar:

> The best Training accuracy = 89.734%

> The best validation accuracy = 94.369%

> The best Test accuracy = 74.266%

Lanjutan model transfer learning dibahas pada part berikut:

ResNet50

MobileNetV2

--

--