Prediksi Diabetes Menggunakan Machine Learning

Dari sini saya belajar bagaimana cara mendapatkan dataset, mempersiapkan dataset, memilih algoritma, melatih model, dan memvalidasi model yang dibuat dengan studi kasus prediksi diabetes.

Prediksi Diabetes Menggunakan Machine Learning


Prediksi Diabetes Menggunakan Machine Learning

Apa saja yang dibutuhkan:

Untuk mendapatkan dataset, teman-teman bisa mencarinya di Google dengan kata kunci "dataset [topik]", contohnya seperti "dataset diabetes", nanti Google akan menampilkan berbagai macam hasil, dan dari hasil tersebut kita bisa mendapatkan Pima Indians Diabetes Database dari situs Kaggle.

Untuk dataset yang saya gunakan, saya sudah modifikasi terlebih dahulu agar bisa melakukan beberapa proses dalam persiapan data. Dataset yang sudah dimodifikasi bisa didapatkan di repositori Github saya.

Selanjutnya kita buka Jupyter Notebook atau bisa juga online lewat Kaggle.

Persiapan Data


Pada proses ini kita akan memeriksa apakah ada data yang duplikat, kosong, melakukan pembersihan data sehingga data yang digunakan menjadi rapi. Data yang rapi akan memudahkan dalam memanipulasi data.

Impor library yang digunakan, lalu load dataset-nya.

# Import library
import pandas as pd # data frame library
import matplotlib.pyplot as plt # untuk plotting
import numpy as np

# Load data csv
df = pd.read_csv('E:\PYTHON\Predict_diabetes\diabetes.csv')

Untuk melihat ukuran dataset, bisa dengan:

# lihat ukuran data
df.shape

Hasil:

(768, 10)

Dari hasil tersebut menunjukkan bahwa terdapat 768 baris/observasi dan 10 kolom/variabel/fitur.

Untuk melihat n data awal, bisa dengan:

# liat 5  data awal 
df.head(5)

Hasil:

Prediksi Diabetes Menggunakan Machine Learning

Kita juga bisa melihat n data akhir, dengan:

# liat 5 data bawah
df.tail(5)

Hasil:

Prediksi Diabetes Menggunakan Machine Learning

Mari kita cek apakah terdapat data yang kosong.

# apakah ada data yg kosong?
df.isnull().values.any()

Hasil:

False

Dari hasil tersebut, bisa diketahui bahwa dataset tidak memiliki data yang kosong.

Selanjutnya kita cek korelasi antar kolom. Cek korelasi ini bertujuan untuk melihat apakah ada data yang sama atau tidak. 

# cek kolerasi, data repetisi atau sama
def plot_corr(df, size=11):
corr = df.corr() # data frame corelation
fig, ax = plt.subplots(figsize=(size,size))
ax.matshow(corr)
plt.xticks(range(len(corr.columns)), corr.columns)
plt.yticks(range(len(corr.columns)), corr.columns)

plot_corr(df)

Hasil:

Prediksi Diabetes Menggunakan Machine Learning

Dari gambar di atas, bisa dilihat bahwa kotak yang berwarna kuning menunjukkan adanya data yang sama atau duplikat. 

Contoh: baris Pregnancies dan kolom Pregnancies akan berwarna kuning karena memiliki data yang sama, begitu juga dengan kolom dan baris yang memiliki nama yang sama, sehingga kotak warna kuning membentuk pola diagonal. 

Namun jika dilihat kembali, terdapat data yang sama pada kolom SkinThickness dan baris Skin, juga sebaliknya.

Mari kita lihat nilai korelasinya.

# lihat korelasi
df.corr()

Hasil:

Prediksi Diabetes Menggunakan Machine Learning

Apabila kita bandingkan nilai korelasi antara SkinThickness dan Skin, maka terlihat bahwa kedua kolom tersebut memiliki nilai yang sama sehingga kita bisa menghapus salah satu kolom tersebut.

Misalnya kita hapus kolom Skin.

# krn korelasi SkinThickness dan Skin sama, hapus salah satunya, hapus Skin
del df['Skin']

Cek apakah kolom Skin sudah terhapus.

# cek apakah sudah terhapus?
df.head(5)

Hasil:

Prediksi Diabetes Menggunakan Machine Learning

Kolom Skin sudah terhapus, sekarang kita cek kembali korelasinya.

# cek kembali korelasi
plot_corr(df)

Hasil:

Prediksi Diabetes Menggunakan Machine Learning

Dari gambar di atas, bisa dilihat bahwa tidak ada lagi data yang sama atau duplikat.

Sekarang, cek tipe data pada dataset tersebut, pastikan semuanya dalam bentuk numeric.

Jika kita lihat kembali dari data di atas, kolom Outcome masih berisikan True atau False. Mari kita ubah ke numeric, True diganti 1 dan False diganti 0.

# ganti outcome ke 1 atau 0
diabetes_map = {True: 1, False:0} # buat map
df['Outcome'] = df['Outcome'].map(diabetes_map) # ganti ke 1 atau 0

Sekarang cek apakah sudah diganti.

# cek apakah sudah terganti?
df.head(5)

Hasil:

Prediksi Diabetes Menggunakan Machine Learning

Nilai pada kolom Outcome sudah diganti menjadi 1 dan 0.

Kita cek apakah terdapat kesalahan ketika proses penggantian nilai tersebut. Misal, terdapat kata true atau false (huruf awal tidak kapital) sehingga nilai tidak menjadi 0 atau 1, melainkan menjadi NaN.

# cek apakah ada data yang kosong
df.isnull().values.any()

Hasil:

False

Sekarang cek jumlah yang terkena dan tidak terkena diabetes.

# cek distribusi Outcome
# nantinya akan displit untuk training dan testing
num_obs = len(df)
num_true = len(df.loc[df['Outcome'] == 1])
num_false = len(df.loc[df['Outcome'] == 0])
print("Jumlah terkena diabetes: {0} ({1:2.2f}%)".format(num_true, (num_true/num_obs)*100))
print("Jumlah tidak terkena diabetes: {0} ({1:2.2f}%)".format(num_false, (num_false/num_obs)*100))

Hasil:

Jumlah terkena diabetes: 268 (34.90%)
Jumlah tidak terkena diabetes: 500 (65.10%)

Memilih Algoritma


Ada beberapa faktor yang menentukan pemilihan algoritma, diantaranya:
  • Learning type (supervised, unsupervised, atau reinforcement): karena kita mendapatkan dataset yang sudah ada hasilnya, maka kita pilih supervised.
  • Result (regression atau classification): karena hasinya adalah terkena atau tidak terkena diabetes, maka pilih classification.
  • Complexity: karena masih algoritma awal, maka pilih algoritma yang sederhana.
  • Basic atau Enhance: karena masih awal pemilihan algoritma, maka pilih yang basic terlebih dahulu setelah itu mungkin bisa di-enhance.

Dari sini kita sudah menentukan bahwa kita akan memilih algoritma dengan tipe supervised, hasil yang berbentuk klasifikasi, jenis algoritma sederhana dan basic.

Selanjutnya, kita bisa mengumpulkan kandidat algoritma yang mungkin bisa digunakan, diantaranya:
  • Naive Bayes: Memprediksi dengan mempelajari dari data yang ada sebelumnya.
  • Logistic Regression: Berdasarkan weighting, setiap fitur akan diberikan bobot, bobot akan diproses Logistic Regression sehingga mendapatkan nilai yang lebih dekat ke 0 atau 1. 
  • Decision Tree: Mirip seperti if else, setiap node memiliki decision masing-masing.

Dari ketiga kandidat algoritma tersebut, kita akan pilih Naive Bayes terlebih dahulu, karena:
  • Berbasis probabilitas: memprediksi berdasarkan pengalaman sebelumnya, apakah orang tersebut terkena atau tidak terkena diabetes berdasarkan data-data sebelumnya.
  • Membutuhkan sedikit data.
  • Sederhana, mudah untuk dipahami, performa yang cepat dan efisien.

Melatih Model


Sebelum digunakan untuk klasifikasi, model akan dilatih terlebih dahulu sehingga menghasilkan model spesifik yang kita inginkan.

Pelatihan model ini bisa dilakukan berulang kali, misalnya jika ada data tambahan atau perbaikan data, atau jika nanti hasilnya kurang puas, maka bisa mengganti algoritma yang digunakan dan melakukan pelatihan kembali sehingga hasil klasifikasi menjadi lebih baik.

Pada proses ini kita akan menggunakan library Scikit-learn.

Membagi Data


Dataset akan dibagi menjadi 2 bagian, yaitu 70% untuk pelatihan, dan 30% untuk pengujian.

# import library
from sklearn.model_selection import train_test_split

# dapatkan kolom nama fitur
feature_col_names = list(df.columns[0:8])

# dapatkan kolom nama klas/outcome
predicted_class_name = list(df.columns)[8]

# predictor feature column, shape 8*m
X = df[feature_col_names].values

# predicted class, shape 1*m
y = df[predicted_class_name].values

# split data, 30% test, 70% train
split_test_size = .3
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=split_test_size, random_state=42)

Cek hasil split-nya.

# cek hasil split
print("{0:0.2f}% ada pada training set".format((len(X_train)/len(df.index)) * 100))
print("{0:0.2f}% ada pada test set".format((len(X_test)/len(df.index)) * 100))
print("")
print("Jumlah original terkena diabetes: {0} ({1:2.2f}%)".format(len(df.loc[df['Outcome'] == 1]), (len(df.loc[df['Outcome'] == 1])/len(df.index))* 100))
print("Jumlah original tidak terkena diabetes: {0} ({1:2.2f}%)".format(len(df.loc[df['Outcome'] == 0]), (len(df.loc[df['Outcome'] == 0])/len(df.index))* 100))
print("")
print("Training True: {0} ({1:2.2f}%)".format(len(y_train[y_train[:] == 1]), (len(y_train[y_train[:] == 1]) / len(y_train) * 100)))
print("Training False: {0} ({1:2.2f}%)".format(len(y_train[y_train[:] == 0]), (len(y_train[y_train[:] == 0]) / len(y_train) * 100)))
print("")
print("Testing True: {0} ({1:2.2f}%)".format(len(y_test[y_test[:] == 1]), (len(y_test[y_test[:] == 1]) / len(y_test) * 100)))
print("Testing False: {0} ({1:2.2f}%)".format(len(y_test[y_test[:] == 0]), (len(y_test[y_test[:] == 0]) / len(y_test) * 100)))

Hasil:

69.92% ada pada training set
30.08% ada pada test set

Jumlah original terkena diabetes: 268 (34.90%)
Jumlah original tidak terkena diabetes: 500 (65.10%)

Training True: 188 (35.01%)
Training False: 349 (64.99%)

Testing True: 80 (34.63%)
Testing False: 151 (65.37%)

Pra-pengolahan


Sekarang kita lakukan pra-pengolahan pada data tersebut. Di sini kita akan melihat banyaknya data yang bernilai 0 dan menentukan harus diapakan data tersebut.

Mari kita lihat seberapa banyak data yang berisikan nilai 0.

#lihat data yg 0
print("# rows in dataframe {0}".format(len(df)))
for n in feature_col_names:
print("# rows missing {0}: {1}".format(n, len(df.loc[df[n] == 0])))

Hasil:

# rows in dataframe 768
# rows missing Pregnancies: 111
# rows missing Glucose: 5
# rows missing BloodPressure: 35
# rows missing SkinThickness: 227
# rows missing Insulin: 374
# rows missing BMI: 11
# rows missing DiabetesPedigreeFunction: 0
# rows missing Age: 0

Untuk menangani nilai 0, kita bisa melakukan:
  • Hapus kolom/fitur tersebut.
  • Ganti nilainya.
  • Didiamkan saja.

Dari hasil di atas, kita bisa lihat bahwa banyak sekali data 0 pada setiap fitur. Contohnya pada Insulin, hampir 50% datanya bernilai 0, dengan jumlah yang segini banyaknya kita tidak bisa menghapus atau membiarkan data tersebut.

Maka kita bisa menggantinya dengan nilai mean.

# mean inputing
from sklearn.impute import SimpleImputer
fill_0 = SimpleImputer(missing_values=0, strategy="mean")
X_train = fill_0.fit_transform(X_train)
X_test = fill_0.fit_transform(X_test)

Sekarang cek apakah masih ada nilai 0.

dff = pd.DataFrame(X_train)
# cek nilai 0
for n in dff.columns:
print("kolom ke-{0}, jml data 0: {1}".format(n ,len(dff.loc[dff[n] == 0])))

Hasil:

kolom ke-0, jml data 0: 0
kolom ke-1, jml data 0: 0
kolom ke-2, jml data 0: 0
kolom ke-3, jml data 0: 0
kolom ke-4, jml data 0: 0
kolom ke-5, jml data 0: 0
kolom ke-6, jml data 0: 0
kolom ke-7, jml data 0: 0

Sekarang kita bisa lihat bahwa sudah tidak ada lagi data yang bernilai 0.

Melatih Model


Untuk melatih model, bisa dengan.

# import naive bayes
from sklearn.naive_bayes import GaussianNB

# buat model naive bayes
nb_model = GaussianNB()

# train model naive bayes
nb_model.fit(X_train, y_train.ravel())

Evaluasi Model


Sekarang kita evaluasi model yang sudah dilatih dengan data pengujian. 

# prediksi test
nb_predict_test = nb_model.predict(X_test)

Untuk melihat hasilnya, kita bisa menggunakan confusion matrix.

# confusion matrix
print("Confusion Matrix")
print("{0}".format(metrics.confusion_matrix(y_test, nb_predict_test)))
print("")
print("Classification Report")
print(metrics.classification_report(y_test, nb_predict_test))

Hasil:

Prediksi Diabetes Menggunakan Machine Learning

Confusion matrix yang dihasilkan Scikit-learn, bentuknya seperti ini:

Prediksi Diabetes Menggunakan Machine Learning

Kita bisa membacanya:
  • True, berarti hasil klasifikasi benar.
  • False, berarti hasil klasifikasi palsu.
  • Positive, berarti terkena diabetes.
  • Negative, berarti tidak terkena diabetes.

Maka:
  • True Positive (TP), benar terkena diabetes, orang tersebut memang terkena diabetes. TP = 52.
  • True Negative (TN), benar tidak terkena diabetes, orang tersebut memang tidak terkena diabetes. TN = 118.
  • False Positive (FP), palsu terkena diabetes, orang tersebut tidak terkena diabetes namun hasil klasifikasi menunjukkan bahwa dia terkena diabetes. FP = 33.
  • False Negative (FN), palsu tidak terkena diabetes, orang tersebut sebenarnya terkena diabetes namun hasil klasifikasi menunjukna bahwa dia tidak terkena. FN = 28.

Untuk menghitung performa, kita bisa menggunakan:

Akurasi, adalah perbandingan prediksi benar dengan keseluruhan prediksi. Berapa persen prediksi orang yang benar terkena diabetes dan benar tidak terkena diabetes dari keseluruhan orang?

Prediksi Diabetes Menggunakan Machine Learning

Akurasi = ((52+118)/(52+118+33+28))*100 % = 73.59 % 

Presisi, adalah perbandingan prediksi benar positif dengan keseluruhan hasil yang diprediksi positif. Berapa persen prediksi orang yang benar terkena diabetes dengan keseluruhan orang yang diprediksi diabetes?

Prediksi Diabetes Menggunakan Machine Learning

Presisi = (52/(52+33))*100 % = 61.18 %

Recall (Sensitifitas), adalah perbandingan prediksi benar positif dengan keseluruhan data yang benar positif. Berapa persen prediksi orang yang benar terkena diabetes dengan keseluruhan orang memang terkena diabetes?

Prediksi Diabetes Menggunakan Machine Learning

Recall = (52/(52+28))*100 % = 65 %

Specificity, adalah perbandingan prediksi benar negatif dengan keseluruhan data negatif. Dari semua orang yang sehat, berapa orang yang terprediksi sehat?

Prediksi Diabetes Menggunakan Machine Learning

Specificity = (118/(118+33))*100 % = 78.15 %

F1 Score, perbandingan rata-rata presisi dan recall yang dibobotkan.

Prediksi Diabetes Menggunakan Machine Learning

F1 Score = (2*(65%*61%)/(65%+61%))*100 % = 62.94 %

Memilih Acuan Performa Algoritma


Idealnya, apapun algoritma yang digunakan, jika ingin hasilnya bagus, maka FP dan FN harus bernilai 0.

Apabila kita ingin mengevaluasi atau menguji, membandingkan algoritma satu dengan algoritma yang lain, apa yang bisa kita pilih sebagai acuan?

Pilih akurasi apabila FN dan FP memiliki nilai yang simetris atau nilainya hampir sama. Jika nilai FN dan FP berbeda, maka lebih baik pilih F1 Score.

Pilih recall apabila kondisi FP lebih baik dibandingkan kondisi FN. Misalnya pada studi kasus diabetes, lebih baik diprediksi orang yang terkena diabetes tetapi aslinya sehat, daripada diprediksi sehat tetapi orang tersebut menderita diabetes. 

Pilih precision apabila ingin hasilnya benar-benar mementingkan TP. Misalnya, lebih baik ada spam masuk ke inbox dibandingkan email reguler yang dimasukan ke kotak spam.

Pilih specificity apabila ingin hasilnya benar-benar mementingkan TN. Misalnya test narkoba, jangan sampai orang yang tidak memakai malah diprediksi memakai narkoba, sehingga bisa saja dimasukkan ke penjara.  

Dari pertimbangan di atas, acuan mana yang bisa kita pilih? 

Kita bisa memilih acuan recall karena lebih baik orang sehat yang terprediksi diabetes dibandingkan sebaliknya. Untuk nilai acuannya, katakanlah algoritma akan dikatakan bagus apabila recall memiliki nilai lebih dari 70%.

Jika kita lihat kembali hasil evaluasi di atas, nilai recall-nya adalah 65 %. Hasil ini lebih rendah dari nilai acuan, yaitu 70%. Namun, hasil ini wajar karena kita belum melakukan peningkatan performa, atau mencoba algoritma lain yang mungkin bisa lebih baik hasilnya. 

Meningkatkan Performa Prediksi


Mari kita coba menggunakan algoritma lain, yaitu Random Forest Classifier.

# import Random Forest Classifier
from sklearn.ensemble import RandomForestClassifier

# buat model rf
rf_model = RandomForestClassifier(random_state=42, n_estimators=10)

# train model rf
rf_model.fit(X_train, y_train.ravel())

Sekarang kita coba pakai untuk memprediksi

# prediksi test
rf_predict_test = rf_model.predict(X_test)

Tampilkan confusion matrix-nya.

# confusion matrix
print("Confusion Matrix")
print("{0}".format(metrics.confusion_matrix(y_test, rf_predict_test)))
print("")
print("Classification Report")
print(metrics.classification_report(y_test, rf_predict_test))

Hasil:

Prediksi Diabetes Menggunakan Machine Learning

Ternyata nilai recall-nya adalah 54% masih di bawah 70%, dan lebih rendah dibandingkan hasil dari algoritma Naive Bayes.

Sekarang kita coba memakai Algoritma Logistic Regression.

# import logistic regression
from sklearn.linear_model import LogisticRegression

# buat model lr
lr_model = LogisticRegression(C=0.7, random_state=42, solver="liblinear", max_iter=10000)

# train model lr
lr_model.fit(X_train, y_train.ravel())

# prediksi test
lr_predict_test = lr_model.predict(X_test)

# confusion matrix
print("Confusion matrix")
print(metrics.confusion_matrix(y_test, lr_predict_test))
print("")
print("Classification Report")
print(metrics.classification_report(y_test, lr_predict_test))

Hasilnya:

Prediksi Diabetes Menggunakan Machine Learning

Nilai recall-nya adalah 55% masih dibawah 70%. Mungkin kita bisa coba untuk mengatur dataset lagi.

Kalau kita lihat kembali ke atas, jumlah orang terkena dan tidak terkena diabetes jumlahnya tidak sama. Jumlah orang yang terkena diabetes adalah 268 orang, sedangkan jumlah orang yang tidak terkena adalah 500 orang, maka kita bisa atur agar seimbang.

Kita bisa mengatur parameter agar claass weight-nya balanced

Dan kita bisa coba untuk mencari nilai parameter C teroptimal untuk mendapatkan nilai recall yang terbaik. Kita bisa melakukannya dengan perulangan.

# mencari nilai C berdasarkan recall terbaik

# nilai C awal
C_start = 0.01

# nilai C akhir
C_end = 5

# nilai increment C
C_inc = 0.01

# untuk menyimpan nilai C dan recall
C_values, recall_scores = [], []

# untuk menyimpan nilai C
C_val = C_start

# untuk menyimpan nilai recall terbaik
best_recall_score = 0

while (C_val < C_end):
# masukan nilai C saat ini ke C_values
C_values.append(C_val)

# buat model lr dengan nilai C saat ini
lr_model_loop = LogisticRegression(C=C_val, class_weight="balanced", random_state=42, solver="liblinear", max_iter=10000)

# latih model
lr_model_loop.fit(X_train, y_train.ravel())

# prediksi test
lr_predict_loop_test = lr_model_loop.predict(X_test)

# memperoleh nilai recall
recall_score = metrics.recall_score(y_test, lr_predict_loop_test)

# simpan nilai recall saat ini
recall_scores.append(recall_score)

# ambil nilai recall terbaik
if (recall_score > best_recall_score):
best_recall_score = recall_score
best_lr_predict_test = lr_predict_loop_test

#increment nilai C
C_val = C_val + C_inc

# ambil nilai C teroptimal berdasarkan nilai recall terbaik
best_score_C_val = C_values[recall_scores.index(best_recall_score)]

# tampilkan recall terbaik dan C terbaik
print("best recall {0:.3f} occured at C={1:.3f}".format(best_recall_score, best_score_C_val))

# plot recal dan c
plt.plot(C_values, recall_scores, "-")
plt.xlabel("C values")
plt.ylabel("recall score")

# buat model lr dengan nilai C terbaik
lr_model = LogisticRegression(class_weight="balanced", C=best_score_C_val, random_state=42, solver="liblinear")

# latih model
lr_model.fit(X_train, y_train.ravel())

# prediksi test
lr_predict_test = lr_model.predict(X_test)

print("confusion matrix")
print(metrics.confusion_matrix(y_test, lr_predict_test))
print("")
print("Classification Report")
print(metrics.classification_report(y_test, lr_predict_test))

Hasilnya:

Prediksi Diabetes Menggunakan Machine Learning

Prediksi Diabetes Menggunakan Machine Learning

Kita bisa mendapatkan nilai recall terbaik 75% dengan nilai parameter C = 0.32. Hasil ini jauh lebih baik dibandingkan hasil-hasil sebelumnya yang nilai recall-nya hanya 55%.

Cross Validation


Pada proses sebelumnya, evaluasi yang telah kita kerjakan adalah menggunakan split validation, dataset dibagi 70% untuk training dan 30% untuk testing, pemilihan datanya secara acak.

Sekarang kita coba memakai K-fold cross validation, metode validasi ini cocok digunakan ketika kita tidak memiliki banyak data untuk melakukan pembagian data (training dan testing) tanpa kehilangan performa model yang signifikan.

Data akan dibagi menjadi K bagian, nilai K yang akan digunakan adalah 10, sehingga nantinya akan terdapat 10 bagian, 9 digunakan untuk training, dan 1 digunakan untuk testing. Setiap iterasi, data testing akan berpindah ke data selanjutnya. Illustrasinya seperti ini:

Prediksi Diabetes Menggunakan Machine Learning

Sekarang kita coba terapkan.

# import logistic regression cv
from sklearn.linear_model import LogisticRegressionCV

# buat model lr cv
lr_cv_model = LogisticRegressionCV(n_jobs=-1, random_state=42, Cs=3, cv=10, refit=False, class_weight="balanced", max_iter=10000)

# latih model lr cv
lr_cv_model.fit(X_train, y_train.ravel())

# predict test
lr_cv_predict_test = lr_cv_model.predict(X_test)

print("Confusion Matrix")
print(metrics.confusion_matrix(y_test, lr_cv_predict_test))
print("")
print("Classification Report")
print(metrics.classification_report(y_test, lr_cv_predict_test))

Hasil:

Prediksi Diabetes Menggunakan Machine Learning

Dari hasil tersebut bisa dilihat bahwa nilai recall adalah 66% lebih rendah dibandingkan ketika memakai split validation. Namun hasil ini kemungkinan masih bisa ditingkatkan lagi dengan mengatur parameter-parameternya.

Mari kita rangkum hasil dari beberapa algoritma yang sudah digunakan:

AlgoritmaRecall
Naive Bayes65%
Random Forest54%
Logistic Regression55%
Logistic Regression (balanced, optimal C value)75%
Logistic Regression CV66%

Mungkin sekian dari postingan ini, dan terimakasih yang sudah membaca.
Click here to Download