Random Forest untuk Model Klasifikasi Menggunakan Scikit-Learn

Random Forest untuk Pemodelan Klasifikasi

Sekilas Random Forest

Random Forest adalah model ensemble berbasis pohon yang populer pada machine learning. Model ini diperkenalkan oleh Leo Breiman pada Tahun 2001. Random Forest dapat diterapkan pada pemodelan regresi maupun klasifikasi. Pada model random forest untuk regresi prediksi dihitung berdasarkan nilai rata-rata (averaging) dari output setiap decision tree (pohon keputusan). Sedangkan untuk model klasifikasi, prediksi ditentukan menggunakan suara terbanyak (majority vote). Contohnya, model Random Forest dengan 100 pohon, 72 pohon memprediksi data tertentu sebagai kelas A dan 28 pohon memprediksi masuk kelas B, maka data tersebut akan dipredksi sebagai kelas A.

Ilustrasi Model Random Forest

Model Random Forest menggunakan metode bootstrap dalam proses pembentukan setiap pohon. Artinya, dataset yang digunakan oleh setiap pohon bukanlah dataset yang sama, melainkan hasil bootstrap. Splitting node pada Random Forest hanya menggunakan sebagian features saja tidak seperti model bagging. Jumlah yang biasa dipakai yaitu $\sqrt{p}$ atau $log_2{~p}$, dimana p adalah banyaknya features pada dataset. Kedua hal ini, meningkatkan keacakan pada setiap pohon serta independensi antar pohon menjadi semakin tinggi. Breiman menunjukkan bahwa hasil Random Forest kompetitif dibandingkan boosting dan adaptive bagging serta mampu mereduksi bias khususnya pada pemodelan klasifikasi.

Lihat: Random Forest untuk Model Regresi

Random Forest untuk Klasifikasi

Dataset yang akan digunakan pada contoh ini adalah data wine quality dengan peubah target multikelas (3 kelas) yaitu HIGH, STANDARD dan LOW. Dataset ini sudah dalam kondisi ‘clean’ sehingga tulisan ini akan fokus bagaimana mencari model random forest terbaik.

Analisis Deskriptif

Python

import pandas as pd
import numpy as np

data = pd.read_csv('https://raw.githubusercontent.com/sainsdataid/dataset/main/wine-quality-multiclass.csv')

data.info()

Output

# Output

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1143 entries, 0 to 1142
Data columns (total 12 columns):
 #   Column                Non-Null Count  Dtype  
---  ------                --------------  -----  
 0   fixed acidity         1143 non-null   float64
 1   volatile acidity      1143 non-null   float64
 2   citric acid           1143 non-null   float64
 3   residual sugar        1143 non-null   float64
 4   chlorides             1143 non-null   float64
 5   free sulfur dioxide   1143 non-null   float64
 6   total sulfur dioxide  1143 non-null   float64
 7   density               1143 non-null   float64
 8   pH                    1143 non-null   float64
 9   sulphates             1143 non-null   float64
 10  alcohol               1143 non-null   float64
 11  quality               1143 non-null   object 
dtypes: float64(11), object(1)
memory usage: 107.3+ KB

Dataset ini terdiri dari 1143 observasi. Terdapat 10 peubah prediktor dan seluruhnya bertipe numerik. Kolom quality merupakan peubah respon dan berisi nilai HIGH, STANDARD atau LOW yang mengindikasikan kualitas wine tersebut.

Python

# menghitung jumlah data menurut kelas
grup = data["quality"].value_counts()

print(grup)

# menghitung proporsi setiap kelas
grup_prop = grup / len(data)*100

print(grup_prop)

Output

# Output


quality          
HIGH          159
LOW           522
STANDARD      462


quality            
HIGH      13.910761
LOW       45.669291
STANDARD  40.419948

Dari total 1143 data, 522 data atau sekitar 45,7% memiliki label LOW , sementara untuk label STANDARD terdiri dari 462 data atau sekitar 45,7% sisanya 159 (13,9%) memiliki label HIGH. Dari hasil ini terlihat terdapat kondisi yang tidak begitu seimbang khususnya pada kelas HIGH. Hal ini kemungkinan akan mempengaruhi kinerja model khususnya dalam memprediksi data yang seharusnya masuk kelas HIGH tersebut.

Selanjutnya kita menampilkan beberapa statistik deskriptif dari dataset ini:

Python

summary = data.groupby('quality').mean().transpose()

print(summary)

Output

quality                    HIGH        LOW   STANDARD
fixed acidity          8.846541   8.142146   8.317749
volatile acidity       0.395314   0.596121   0.504957
citric acid            0.391195   0.235096   0.263680
residual sugar         2.748428   2.543582   2.444805
chlorides              0.074711   0.092117   0.085281
free sulfur dioxide   14.188679  16.404215  15.215368
total sulfur dioxide  36.672956  54.016284  39.941558
density                0.996019   0.997054   0.996610
pH                     3.282453   3.308410   3.323788
sulphates              0.745849   0.614195   0.676537
alcohol               11.528407   9.922510  10.655339

Kita juga dapat melihat bagaimana karakteristik masing-masing kelas berdasarkan nilai fitur-fiturnya. Misalkan rata-rata kadar alcohol pada kelas HIGH terlihat lebih tinggi dibandingkan kelas STANDARD dan LOW. Sebaliknya rata-rata kadar total.sulfur.dioxide pada kelas HIGH jauh lebih rendah dibandingkan dua kelas lainnya. Contoh lainnya pada pH terlihat ketiga kelas hampir tidak memiliki perbedaan. Artinya kemungkinan fitur ini tidak memiliki kontribusi besar untuk membedakan ketiga kelas.

Jika diperlukan, dapat pula dibuat plot sebarannya untuk masing-masing kelas untuk melihat perbandingan secara visual. Misalkan pada contoh di bawah ini kita menyajikan boxplot untuk sebaran nilai alcohol dan pH menurut kelasnya. Dari visualisasi tersebut terlihat bahwa cenderung terdapat perbedaan nilai alcohol dimana nilai pada kelas HIGH relatif lebih tinggi dibandingkaan kelas STANDARD, begitu juga untuk kelas LOW dimana nilainya relatif lebih rendah dibandingkan kelas lainnya. Sementara itu, pada boxplot nilai pH sebaran nilainya terlihat tidak terlalu berbeda antar ketiga kelas, hal ini bisa menjadi indikasi kemungkinan fitur ini tidak memiliki pengaruh yang besar dalam menentukan kelas data.

Python

import matplotlib.pyplot as plt
import seaborn as sns

# Membuat plot boxplot untuk kolom alcohol
plt.figure(figsize=(8, 4))
sns.boxplot(x='quality', y='alcohol', hue='quality', data=data)
plt.title('Sebaran nilai alcohol Menurut quality')
plt.show()

# Membuat plot boxplot untuk kolom pH
plt.figure(figsize=(8, 4))
sns.boxplot(x='quality', y='pH', hue='quality', data=data)
plt.title('Sebaran nilai pH Menurut quality')
plt.show()

Output

Boxplot sebaran nilai

Pembagian Data Latih dan Data Uji

Pembagian data latih dan uji penting dalam pemodelan machine learning. Data latih digunakan untuk proses training sebagai dasar pembentukan model. Selanjutnya, data uji digunakan untuk mengevaluasi model. Penggunaan data uji diperlukan agar model dapat evaluasi pada data yang belum pernah dilihat sebelumnya.

Fungsi train_test_split pada modul sklearn.model_selection dapat digunakan untuk pembagian data. Pada contoh ini akan digunakan proporsi 70% data latih dan 30% data uji. Pembagian dilakukan secara acak dan proporsional (stratified random sampling), untuk menjaga keterwakilan kedua kelas quality pada data latih dan uji.

Python

from sklearn.model_selection import train_test_split

X = data.drop('quality', axis=1)
y = data['quality']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, 
                                                    stratify=y, random_state=100)

Dari ahsil pembagian data, proporsi masing-masing kelas pada data latih dan data uji relatif sama, yaitu pada kisaran 46%, 40% dan 14%. Hal ini cukup penting agar seluruh kelas memiliki representasi yang memadai baik pada data latih maupun data uji.

Python

# Data latih
grup_train = y_train.groupby(y_train).count() / len(y_train) * 100
grup_test = y_test.groupby(y_test).count() / len(y_test) * 100

print(f"\nProp Data latih:\n{grup_train}")
print(f"\nProp Data Uji:\n{grup_test}")

Output

# Output
Prop Data latih:
quality
HIGH        13.875
LOW         45.625
STANDARD    40.500
Name: quality, dtype: float64

Prop Data Uji:
quality
HIGH        13.994169
LOW         45.772595
STANDARD    40.233236
Name: quality, dtype: float64

Membuat Model

Untuk membuat model Random Forest kita akan menggunakan fungsi RandomForestClassifier dari modul sklearn.ensemble. Beberapa parameter penting pada model yaitu:

  • n_estimators: jumlah pohon yang akan digunakan (default = 100)
  • criterion: {“gini”, “entropy”, “log_loss”}, default=”gini”. Parameter ini digunakan untuk menentukan kriteria untuk memperoleh splitting terbaik pada setiap node.
  • max_depth, default=None. Maksimal kedalaman pada setiap pohon. None menunjukkan kedalaman pohon tidak dibatasi (dibatasi menurut kriteria paramater lainnya seperti min_samples_split dan min_samples_leaf.
  • min_samples_split, default=2. Minimal jumlah data pada suatu node agar node tersebut bisa di-split.
  • min_samples_leaf, default=1. Minimal jumlah data pada masing-masing leaf agar suatu node bisa di-split.
  • max_features: {“sqrt”, “log2”, None}, default=”sqrt”. Maksimal jumlah features yang digunakan dalam proses penentuan split terbaik pada setiap node.
  • dll

Pada bagian ini kita gunakan nilai parameter n_estimators=200, min_samples_split=5 dan class_weight="balanced". Nilai "balanced" pada parameter class_weight akan meningkatkan bobot kelas minoritas dan mengurangi bobot kelas mayoritas dalam menghitung prediksi. Penentuan class_weight bermanfaat pada kondisi kelas tidak seimbang sehingga hasil prediksi menjadi lebih baik khususnya bagi kelas minoritas.

Nilai random_state dapat ditentukan berapa saja. Parameter ini ditetapkan dengan nilai tertentu agar proses pelatihan model dapat di-reproduce dan memperoleh hasil yang sama walaupun dijalankan berulang kali atau dieksekusi pada perangkat yang berbeda.

Python

from sklearn.ensemble import RandomForestClassifier

model_rf = RandomForestClassifier(n_estimators=200, 
                                  min_samples_split=5, 
                                  class_weight="balanced", 
                                  random_state=100)

# fitting model
model_rf.fit(X_train, y_train)

# Memprediksi output dari data uji
y_pred = model_rf.predict(X_test)

print(y_pred)

Output

# Output

array(['LOW', 'LOW', 'STANDARD', 'LOW',  ..., 'LOW', 'LOW', 'HIGH'],
      dtype=object)

Setelah memperoleh model Random Forest, langkah selanjutnya adalah mengevaluasi model menggunakan data uji. Seperti yang sudah dijelaskan sebelumnya, penggunakan data uji atau data yang belum pernah dilihat model saat proses training akan memberikan evaluasi yang lebih fair dan menghindari overfitting.

Hasil prediksi pada data uji kemudian dibandingkan dengan nilai sebenarnya untuk mengukur seberapa baik model dalam memprediksi data tersebut.

Variabel y_pred berisi prediksi kelas untuk setiap amatan pada data uji secara berurutan. Untuk mengukur seberapa baik model dalam memprediksi kita perlu membandingkan hasil ini dengan label kelas sebenarnya yang tersimpan pada variabel y_test. Terdapat beberapa kriteria yang dapat digunakan dalam pemodelan klasifikasi seperti accuracy, balanced accuracy, f1 score, ROC AUC dan sebagainya.

Python

from sklearn.metrics import (
    confusion_matrix,
    accuracy_score,
    balanced_accuracy_score,
    classification_report,
)

print("\nconfusion_matrix:\n", confusion_matrix(y_test, y_pred))

print("\nAccuracy :", accuracy_score(y_test, y_pred))

print("\nbalanced Accuracy :", balanced_accuracy_score(y_test, y_pred))

print("\nClassification Report:\n",classification_report(y_test, y_pred))

Output

# Output

confusion_matrix:
 [[ 25   3  20]
 [  0 124  33]
 [  8  31  99]]

Accuracy : 0.7230320699708455

balanced Accuracy : 0.6760111849595373

Classification Report:
               precision    recall  f1-score   support

        HIGH       0.76      0.52      0.62        48
         LOW       0.78      0.79      0.79       157
    STANDARD       0.65      0.72      0.68       138

    accuracy                           0.72       343
   macro avg       0.73      0.68      0.70       343
weighted avg       0.73      0.72      0.72       343

Dari informasi di atas diperoleh akurasi model sebesar 0,7230. Nilai ini bermakna berdasarkan data uji model berhasil memprediksi benar sebanyak 72,30 persen data. Pada contoh dari 343 data uji, 248 diantaranya diprediksi benar sementara sisanya sebanyak 95 salah prediksi. Nilai accuracy mengukur kinerja model secara total , pada kasus data yang tidak seimbang, terkadang nilai ini menjadi kurang tepat untuk digunakan. Ukuran lain yang dapat digunakan antara lain adalah balanced accuracy yang sudah mempertimbangkan kinerja setiap kelas. Nilai balance accuracy yang diperoleh sebesar 0,676 dan cukup jauh dibandingkan nilai accuracy mengindikasikan terdapat kelas dengan kinerja yang kurang baik.

Lebih rinci, berdasarkan confussion matrix dan classification report, kelas LOW memiliki nilai recall (sensitivitas) 0,79 di mana dari 157 data uji 124 diprediksi benar sementara 33 lainnya diprediksi salah. Sementara itu, nilai recall pada kelas STANDARD juga relatif sama yaitu sebesar 0,72 di mana dari 138 data uji 99 diprediksi benar dan sisanya salah (8 diprediksi sebagai kelas HIGH dan 31 sebagai kelas LOW). Adapun nilai recall kelas HIGH sangat rendah yaitu hanya 0,52 saja. Dari 48 data uji hanya 25 yang diprediksi benar sementara 23 lainnya salah, atau hanya sekitar setengahnya saja.

Meskipun secara umum model yang diperoleh memiliki kinerja yang cukup baik. Namun terdapat kemungkinan bahwa ada kombinasi hiperparmeter lain yang menghasilkan model lebih baik lagi. Oleh karena itu, pada bagian selanjutnya kita akan melakukan pencarian tersebut atau disebut juga tuning hiperparameter.

Terdapat beberapa teknik tuning hiperparameter yang tersedia pada scikit-learn. Salah satunya adalah random search cross validation yaitu mencari model terbaik berdasarkan kombinasi parameter yang diberikan secara random. Pada scikit-learn, proses ini dapat dilakukan menggunakan fungsi RandomizedSearchCV dari modul sklearn.model_selection. Fungsi randomizedSearchCV sendiri tidak hanya digunakan untuk model Random Forest saja, namun dapat digunakan pula pada model machine learning lainnya.

Pertama kita tentukan daftar nilai parameter yang diinginkan. Setiap parameter dapat memiliki lebih dari 1 nilai. Selanjutnya kita tentukan berapa banyak iterasi yang akan dilakukan. Misalkan 100, maka akan dilakukan iterasi sebanyak 100 kali dengan kombinasi nilai hiperaprameter yang dipilih secara random dari batasan yang diberikan. Pada setiap iterasi dilakukan proses pemodelan dan validasi untuk mengukur kinerja model berdasarkan metrik tertentu. Selanjutnya model dengan kinerja terbaik hasil validasi akan dijadikan sebagai model terbaik. Semakin banyak iterasi maka waktu eksekusi tentunya akan semakin lama, namun peluang memperoleh model yang lebih baik tentunya semakin besar.

Pada bagian ini, misalkan kita akan menggunakan balanced_accuracy sebagai metrik yang diukur. Kemudian, untuk hiperparameter yang akan kita tentukan yaitu n_estimators, max_depth, min_samples_split, max_features dan class_weight. Mengingat kondisi data yang tidak terlalu seimbang penentuan nilai class_weight yang berbeda mungkin dapat membantu meningkatkan performa model.

Python

from sklearn.model_selection import RandomizedSearchCV

param_dist = {
    "n_estimators": np.arange(100, 401, 10),             # 100, 110, 120, .., 400
    "max_depth": [None] + np.arange(1, 20, 2).tolist(),  # None, 1, 3, 5, ... 19
    "min_samples_split": [2, 3, 5, 10, 15],
    "max_features": [1, 2, 3, 4, 5],
    "class_weight": ["balanced", "balanced_subsample"],
}

# tuning model dengan random search (50 iterasi)
rf_cv = RandomizedSearchCV(
    model_rf,
    param_distributions=param_dist,
    n_iter=200,                                          # pencarian sebanyak 200 kali
    scoring="balanced_accuracy",                         # ukuran kinerja dengan balanced_accuracy
    cv=5,                                                # validasi silang dengan 5 fold
    refit=True,
    n_jobs=-1,                                           # gunakan seluruh processor
    random_state=100
)

# fitting model
rf_cv.fit(X_train, y_train)

# menampilkan pengaturan parameter da skor terbaik
print("Best Param:", rf_cv.best_params_)
print("best Score:", rf_cv.best_score_)

Output

# Output

Best Param: {'n_estimators': 280, 'min_samples_split': 10, 'max_features': 4, 'max_depth': 19, 'class_weight': 'balanced_subsample'}

best Score: 0.6553456120512404

Pada kode di atas, kita menentukan beberapa nilai berbeda untuk masing-masing parameter model. Selanjutnya gunakan suatu model Random Forest sebagai basis pencarian model terbaik berdasarkan parameter yang disediakan. Untuk menentukan model terbaik pada contoh ini kita akan menggunakan ukuran scoring="balanced_accuracy". Pemilihan model terbaik akan ditentukan berdasarkan nilai akurasi tertinggi hasil validasi silang pada data latih untuk setiap kombinasi parameter. Nilai lainnya pada fungsi RandomizedSearchCV yang kita tetapkan yaitu cv=5 (jumlah fold untuk proses cross validation), refit=True dan n_jobs=-1 (proses paralel menggunakan semua processor).

Setelah menjalankan kode di atas, kita akan memperoleh model terbaik berdasarkan parameter yang telah ditentukan. Model terbaik yang diperoleh memiliki nilai balanced_accuracy (berdasarkan validasi silang pada data latih) sebesar 0.655. Adapun parameter model hasil tuning berturut-turut yaitu 'n_estimators': 280, 'min_samples_split': 10, 'max_features': 4, 'max_depth': 19, 'class_weight': 'balanced_subsample'.

Seperti pada model sebelumnya, lakukan evaluasi menggunakan data uji.

Python

y_pred_cv = rf_cv.predict(X_test)

print("\nconfusion_matrix:\n", confusion_matrix(y_test, y_pred_cv))

print("\nAccuracy :", accuracy_score(y_test, y_pred_cv))

print("\nbalanced Accuracy :", balanced_accuracy_score(y_test, y_pred_cv))

print("\nClassification Report:\n",classification_report(y_test, y_pred_cv))

Output

# Output

confusion_matrix:
 [[ 29   0  19]
 [  1 126  30]
 [ 12  32  94]]

Accuracy : 0.7259475218658892

balanced Accuracy : 0.6959579525523862

Classification Report:
               precision    recall  f1-score   support

        HIGH       0.69      0.60      0.64        48
         LOW       0.80      0.80      0.80       157
    STANDARD       0.66      0.68      0.67       138

    accuracy                           0.73       343
   macro avg       0.72      0.70      0.70       343
weighted avg       0.73      0.73      0.73       343

Evaluasi model menunjukkan nilai balanced accuracy sebesar 0,696 dan terjadi peningkatan dibandingkan model sebelumnya. Selain itu terjadi peningkatan juga pada nilai accuracy menjadi 0,726. Hasil ini mungkin saja masih bisa ditingkatkan lagi dengan menambah jumlah iterasi pencarian, merubah daftar nilai hiperaprameter ataupun menggunakan teknik tuning hiperparameter lainnya. Namun untuk tulisan ini kita akan anggap hasil ini sebagai model akhir.

Menyimpan dan Memanggil Model

Model yang telah dibuat dapat disimpan dalam suatu file. Model dapat digunakan kembali atau bahkan didistribusikan tanpa perlu mengulang setiap proses training maupun tuning hiperparameter seperti yang sudah dilakukan.

Terdapat beberapa cara untuk melakukan proses ini misalkan menggunakan pustaka pickle atau joblib. Di sini kita akan menggunakan fungsi dump dan load pada pustaka joblib. Untuk menyimpan model ke dalam file dilakukan menggunakan fungsi dump sedangkan fungsi load untuk memuat model. Selanjutnya, model yang sudah dimuat dapat kita gunakan untuk melakukan prediksi data.

Pada contoh di bawah ini, misalkan terdapat dua sampel wine baru, kita dapat melakukan prediksi terhadap dua data tersebut. Hasil prediksi menunjukkan bahwa wine pertama diprediksi sebagai kelas HIGH dan wine kedua masuk ke dalam kelas STANDARD.

Python

from joblib import dump, load

# Menyimpan model ke dalam file
dump(rf_cv, 'model_rf_cv.joblib') 

# Membaca model dari file
model_from_file = load('model_rf_cv.joblib')

# contoh data baru
new_data = pd.DataFrame({'fixed acidity' : [8.4, 7.94],
                         'volatile acidity' : [0.46, 0.48],
                         'citric acid' : [0.32, 0.28],
                         'residual sugar' : [2.6, 2.6],
                         'chlorides': [0.065, 0.081],
                         'free sulfur dioxide': [14.7, 14.7],
                         'total sulfur dioxide': [38.24, 38.85],
                         'density': [0.95, 0.98],
                         'pH': [3.4, 3.4],
                         'sulphates': [0.72, 0.70],
                         'alcohol': [11.8, 11.0]
                        })

print(new_data.transpose())

# Menggunakan model untuk memprediksi
model_from_file.predict(new_data)

Output

# Output

                          0       1
fixed acidity          8.400   7.940
volatile acidity       0.460   0.480
citric acid            0.320   0.280
residual sugar         2.600   2.600
chlorides              0.065   0.081
free sulfur dioxide   14.700  14.700
total sulfur dioxide  38.240  38.850
density                0.950   0.980
pH                     3.400   3.400
sulphates              0.720   0.700
alcohol               11.800  11.000

array(['HIGH', 'STANDARD'], dtype=object)

Selamat mencoba!!!

Referensi

Tulisan Lainnya

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *

Daftar Isi