Machine learning merupakan cabang kecerdasan buatan yang memungkinkan komputer mempelajari pola dari data untuk melakukan prediksi tanpa diprogram secara eksplisit untuk setiap kasus. Dalam statistika, pendekatan ini banyak dipakai untuk klasifikasi yang responsnya berupa kategori dan regresi yang responsnya berupa nilai numerik. Salah satu model yang populer adalah decision tree karena mudah diinterpretasikan, tetapi satu pohon keputusan cenderung tidak stabil: perubahan kecil pada data latih dapat menghasilkan struktur pohon yang jauh berbeda sehingga variance-nya tinggi dan mudah mengalami overfitting.
Untuk mengatasi masalah tersebut, dikembangkan ensemble learning, yaitu pendekatan yang menggabungkan banyak model dasar agar prediksi menjadi lebih akurat dan stabil (Fransiska, 2026). Dua kelompok metode ensemble yang banyak dibahas adalah bagging yang membangun model secara paralel dari sampel bootstrap, dan boosting yang membangun model secara berurutan untuk memperbaiki kesalahan model sebelumnya. Random Forest merupakan pengembangan dari bagging: selain memakai sampel bootstrap, metode ini mengacak prediktor yang dipertimbangkan pada setiap node sehingga pohon-pohonnya tidak terlalu berkorelasi dan penurunan variance menjadi lebih efektif (James dkk., 2021; Biau & Scornet, 2016).
Meskipun efektif, Random Forest masih dapat mengalami underfitting karena setiap pohon hanya dibentuk dari satu sampel bootstrap yang memuat sekitar 63,2% observasi unik. Han, Kim, dan Lee (2020) memperkenalkan Double Random Forest (DRF) yang membangun setiap pohon dari seluruh data latih, sedangkan bootstrap digunakan pada setiap node untuk mencari aturan pemisahan, sehingga pohon yang dihasilkan cenderung lebih besar dan beragam. Pada kedua metode ini, performa model perlu dievaluasi dengan ukuran yang sesuai, misalnya accuracy, precision, recall, specificity, dan F1-score pada klasifikasi, serta Out-of-Bag (OOB) error sebagai evaluasi internal tanpa harus menyisihkan data khusus.
Berdasarkan latar belakang di atas, rumusan masalah yang dapat disimpulkan yaitu:
Berdasarkan rumusan masalah di atas, tujuan yang dapat disimpulkan yaitu:
Adapun manfaat dari praktikum ini yaitu:
Adapun batasan masalah dari praktikum ini, yaitu lakukan analisis
Random Forest secara lengkap mulai dari persiapan data,
pembentukan model, prediksi, evaluasi, visualisasi, dan interpretasi
dengan set.seed(234) agar hasil dapat direproduksi. Lakukan
Random Forest classification pada dataset
BreastCancer, kemudian bandingkan mtry = 2, 3, dan
5 dengan num.trees = 200, 500, dan 1000. Bandingkan
performa pada data testing berdasarkan confusion
matrix, accuracy, precision, recall,
specificity, F1-score, dan variable
importance, serta tampilkan pula OOB error sebagai
evaluasi internal model. Selanjutnya, lakukan Double Random Forest
classification dengan sintaks dari modul pada data dan pembagian
data yang sama, lalu bandingkan hasilnya dengan Random
Forest.
Random Forest merupakan metode ensemble learning berbasis pohon keputusan (decision tree) yang menggabungkan prediksi dari sejumlah pohon untuk menghasilkan prediksi akhir. Metode ini mengombinasikan bootstrap sampling dan pemilihan prediktor secara acak pada setiap node untuk membangun pohon-pohon yang beragam. Penggabungan prediksi tersebut bertujuan mengurangi variansi dan meningkatkan kemampuan generalisasi model (Fransiska, 2026; Wright & Ziegler, 2017).
Menurut Fransiska (2026), proses pembentukan Random Forest melibatkan empat tahapan utama. Pertama, bootstrap sampling, yaitu pengambilan sampel secara acak dengan pengembalian dari data latih. Kedua, pembangunan sejumlah pohon keputusan berdasarkan sampel yang diperoleh. Ketiga, random feature selection, yaitu pemilihan sebagian prediktor secara acak sebagai kandidat pemisahan pada setiap node. Keempat, agregasi prediksi seluruh pohon untuk memperoleh hasil akhir.
Pada setiap pohon, pemilihan prediktor secara acak membantu mengurangi kemiripan antarpohon. Dengan demikian, penggabungan prediksi diharapkan menghasilkan model yang lebih stabil dibandingkan penggunaan satu pohon keputusan saja (James dkk., 2021; Wright & Ziegler, 2017).
Misalkan tersedia data latih
\[ D=\{(x_i,y_i)\}_{i=1}^{n}, \]
dengan \(x_i\) merupakan vektor prediktor, \(y_i\) merupakan variabel respons, dan \(n\) adalah jumlah observasi. Jika dibangun sebanyak \(B\) pohon keputusan, setiap pohon dinyatakan sebagai \(T_b(x)\) untuk \(b=1,2,\ldots,B\).
Berdasarkan jenis variabel respons, Random Forest dapat digunakan untuk dua tujuan utama, yaitu klasifikasi dan regresi.
Random Forest klasifikasi digunakan ketika variabel respons berbentuk kategorik, misalnya status kelulusan mahasiswa, kategori tingkat kesejahteraan, atau status penyakit. Setiap pohon keputusan menghasilkan prediksi kelas berdasarkan karakteristik prediktor yang dimiliki suatu observasi. Prediksi akhir diperoleh melalui majority voting, yaitu pemilihan kelas yang memperoleh suara terbanyak dari seluruh pohon (Fransiska, 2026).
Secara matematis, prediksi kelas pada Random Forest klasifikasi dirumuskan sebagai berikut.
\[ \hat{G}_{RF}(x) = \operatorname{mode} \{T_1(x),T_2(x),\ldots,T_B(x)\}. \]
Keterangan:
Sebagai contoh, apabila 100 pohon menghasilkan 65 prediksi kelas Lulus dan 35 prediksi kelas Tidak Lulus, prediksi akhir observasi tersebut adalah Lulus.
Kinerja model klasifikasi dapat dievaluasi menggunakan confusion matrix dan beberapa ukuran evaluasi, seperti accuracy, precision, recall, specificity, dan F1-score. Penggunaan ukuran evaluasi yang sesuai diperlukan agar kinerja model dapat dinilai secara lebih menyeluruh, terutama ketika distribusi kelas tidak seimbang.
Random Forest regresi digunakan ketika variabel respons berbentuk numerik kontinu, misalnya jumlah pengeluaran, harga rumah, atau nilai produksi. Berbeda dari klasifikasi yang menentukan kelas melalui majority voting, regresi menggabungkan prediksi numerik dari seluruh pohon menggunakan rata-rata (Fransiska, 2026; Wright & Ziegler, 2017).
Secara matematis, prediksi Random Forest regresi dirumuskan sebagai berikut.
\[ \hat{f}_{RF}(x) = \frac{1}{B} \sum_{b=1}^{B}T_b(x). \]
Keterangan:
Sebagai contoh, apabila tiga pohon menghasilkan prediksi sebesar 10, 12, dan 14, prediksi akhir Random Forest regresi adalah
\[ \hat{f}_{RF}(x) = \frac{10+12+14}{3} = 12. \]
Evaluasi model regresi dapat dilakukan menggunakan Mean Squared Error (MSE), Root Mean Squared Error (RMSE), Mean Absolute Error (MAE), dan koefisien determinasi (\(R^2\)). Nilai MSE, RMSE, dan MAE yang lebih kecil menunjukkan kesalahan prediksi yang lebih rendah, sedangkan nilai \(R^2\) yang lebih besar umumnya menunjukkan kemampuan model yang lebih baik dalam menjelaskan variasi respons pada data evaluasi (Fransiska, 2026).
Secara umum, tahapan pembentukan Random Forest dapat dijelaskan sebagai berikut (Fransiska, 2026; Wright & Ziegler, 2017).
Perbedaan mendasar antara Random Forest klasifikasi dan regresi terletak pada jenis respons, kriteria pemisahan, dan mekanisme agregasi prediksi.
| Aspek | Random Forest Klasifikasi | Random Forest Regresi |
|---|---|---|
| Variabel respons | Kategorik | Numerik |
| Tujuan | Memprediksi kelas | Memprediksi nilai numerik |
| Kriteria pemisahan | Misalnya indeks Gini | Misalnya penurunan variansi |
| Agregasi prediksi | Majority voting | Rata-rata prediksi |
| Evaluasi | Accuracy, precision, recall, F1-score | MSE, RMSE, MAE, \(R^2\) |
Out-of-Bag (OOB) merupakan mekanisme evaluasi internal pada Random Forest yang memanfaatkan observasi yang tidak terpilih dalam sampel bootstrap untuk membangun pohon tertentu. Mekanisme ini memungkinkan evaluasi prediksi tanpa harus menggunakan observasi yang sama untuk membentuk dan mengevaluasi pohon tersebut (Fransiska, 2026; Wright & Ziegler, 2017).
Pada pengambilan sampel bootstrap berukuran \(n\) dari \(n\) observasi dengan pengembalian, peluang suatu observasi tidak terpilih dalam satu pengambilan adalah \(1-\frac{1}{n}\). Oleh karena itu, peluang observasi tersebut tidak terpilih dalam seluruh pengambilan adalah
\[ P(\text{OOB}) = \left(1-\frac{1}{n}\right)^n. \]
Ketika \(n\) semakin besar, peluang tersebut mendekati
\[ \lim_{n\to\infty} \left(1-\frac{1}{n}\right)^n = e^{-1} \approx 0{,}368. \]
Dengan demikian, sekitar 36,8% observasi tidak masuk ke dalam sampel bootstrap suatu pohon, sedangkan sekitar 63,2% observasi unik masuk ke dalam sampel tersebut secara rata-rata.
Prediksi OOB untuk setiap observasi dihitung menggunakan pohon-pohon yang tidak menggunakan observasi tersebut dalam proses pembentukannya. Selanjutnya, prediksi OOB digunakan untuk menghitung kesalahan prediksi model.
Pada klasifikasi, kesalahan OOB dapat dinyatakan sebagai proporsi observasi yang salah diklasifikasikan. Pada regresi, kesalahan OOB dapat dihitung menggunakan MSE antara respons aktual dan prediksi OOB.
Penggunaan OOB dapat membantu mengevaluasi model secara internal. Namun, OOB tidak sepenuhnya menggantikan kebutuhan evaluasi pada data uji terpisah, khususnya ketika penelitian memerlukan penilaian kemampuan generalisasi pada data yang belum pernah digunakan selama pengembangan model (Wright & Ziegler, 2017).
Double Random Forest (DRF) merupakan pengembangan metode ensemble yang diperkenalkan oleh Han, Kim, dan Lee (2020). Metode ini dirancang untuk mengatasi kondisi ketika Random Forest belum menghasilkan pohon yang cukup besar untuk memperoleh kinerja prediksi yang optimal. Gagasan utamanya adalah menerapkan bootstrap sampling pada setiap node selama pembentukan pohon, bukan hanya sekali pada akar pohon sebagaimana mekanisme Random Forest standar (Han dkk., 2020; Fransiska, 2026).
Pada Random Forest standar, setiap pohon dibangun menggunakan satu sampel bootstrap yang diambil dari keseluruhan data latih. Sebaliknya, DRF menggunakan seluruh data latih sebagai data awal setiap pohon, kemudian melakukan pengambilan sampel bootstrap secara lokal pada node yang sedang diproses untuk menentukan aturan pemisahan.
Secara umum, tahapan DRF adalah sebagai berikut.
Penerapan bootstrap pada setiap node memungkinkan DRF membentuk pohon yang lebih besar dibandingkan Random Forest standar pada kondisi tertentu. Namun, pohon yang lebih besar tidak selalu menjamin peningkatan akurasi. Kinerja DRF tetap perlu dibandingkan secara empiris dengan Random Forest pada data dan skenario evaluasi yang sama (Han dkk., 2020).
DRF klasifikasi digunakan untuk memprediksi variabel respons kategorik. Dalam metode yang diperkenalkan oleh Han dkk. (2020), setiap pohon dibangun menggunakan mekanisme bootstrap pada setiap node untuk memperoleh aturan pemisahan. Prediksi akhir ditentukan berdasarkan kelas yang memperoleh suara terbanyak dari seluruh pohon.
Secara matematis, prediksi DRF klasifikasi dapat dituliskan sebagai
\[ \hat{G}_{DRF}(x) = \operatorname{mode} \{T_1^{DRF}(x),T_2^{DRF}(x),\ldots,T_B^{DRF}(x)\}. \]
Keterangan:
Kinerja DRF klasifikasi dapat dievaluasi menggunakan accuracy, precision, recall, specificity, dan F1-score. Pemilihan metrik perlu mempertimbangkan karakteristik data, terutama apabila terdapat ketidakseimbangan jumlah observasi antarkelas.
Han dkk. (2020) mengembangkan dan mengevaluasi DRF dalam konteks klasifikasi. Oleh karena itu, temuan penelitian tersebut menjadi landasan utama untuk menjelaskan mekanisme DRF klasifikasi.
DRF regresi secara konseptual ditujukan untuk memprediksi variabel respons numerik dengan memanfaatkan mekanisme pembentukan pohon DRF. Jika mekanisme DRF diterapkan pada pohon regresi, prediksi akhir dapat dirumuskan menggunakan rata-rata prediksi seluruh pohon, sebagaimana agregasi pada metode ensemble regresi.
Secara matematis, bentuk agregasi tersebut dapat dituliskan sebagai
\[ \hat{f}_{DRF}(x) = \frac{1}{B} \sum_{b=1}^{B}T_b^{DRF}(x). \]
Keterangan:
Persamaan tersebut menjelaskan bentuk agregasi prediksi apabila DRF diterapkan untuk regresi. Namun, persamaan ini saja tidak membuktikan bahwa algoritma DRF regresi telah didefinisikan atau divalidasi dalam suatu implementasi tertentu.
Artikel asli Han dkk. (2020) berfokus pada DRF klasifikasi. Dengan demikian, penerapan DRF regresi perlu disertai sumber metodologis dan implementasi yang secara eksplisit mendukung pembentukan pohon regresi menggunakan mekanisme bootstrap pada setiap node. Kriteria pemisahan, aturan penghentian pohon, serta prosedur evaluasinya juga perlu dijelaskan agar metode tersebut dapat direplikasi.
Apabila implementasi DRF regresi yang digunakan telah tervalidasi, evaluasi dapat dilakukan menggunakan MSE, RMSE, MAE, dan \(R^2\). Nilai kesalahan prediksi yang lebih rendah menunjukkan hasil yang lebih baik, sedangkan nilai \(R^2\) yang lebih tinggi umumnya menunjukkan kemampuan model yang lebih baik dalam menjelaskan variasi respons pada data evaluasi.
Perbedaan utama Random Forest dan DRF terletak pada mekanisme pengambilan sampel selama pembentukan pohon. Pada Random Forest, sampel bootstrap diambil sekali untuk setiap pohon. Pada DRF, sampel bootstrap diambil pada setiap node yang diproses. Perbedaan tersebut memengaruhi observasi yang digunakan untuk menentukan aturan pemisahan dan memungkinkan pembentukan pohon yang lebih besar pada kondisi tertentu (Han dkk., 2020).
| Aspek | Random Forest | Double Random Forest |
|---|---|---|
| Data awal setiap pohon | Sampel bootstrap dari data latih | Seluruh data latih |
| Pengambilan sampel | Satu kali untuk setiap pohon | Pada setiap node |
| Pemilihan prediktor | Acak pada setiap node | Acak pada setiap node |
| Ukuran pohon | Bergantung pada parameter dan aturan penghentian | Berpotensi lebih besar pada kondisi tertentu |
| Agregasi klasifikasi | Majority voting | Majority voting |
| Agregasi regresi | Rata-rata prediksi | Rata-rata jika metode diterapkan pada pohon regresi |
| Pertimbangan komputasi | Bergantung pada jumlah dan kompleksitas pohon | Berpotensi memerlukan komputasi lebih besar karena bootstrap berulang |
| Landasan metode | Klasifikasi dan regresi | Artikel asli berfokus pada klasifikasi; regresi memerlukan dukungan metodologis tambahan |
Berdasarkan perbedaan tersebut, DRF dapat dipertimbangkan sebagai alternatif Random Forest, khususnya ketika ukuran pohon menjadi perhatian dalam proses pemodelan. Meskipun demikian, pemilihan metode sebaiknya didasarkan pada hasil evaluasi empiris, bukan hanya pada ukuran pohon yang dihasilkan.
Kinerja model berbasis pohon dipengaruhi oleh parameter yang mengatur jumlah pohon, pemilihan prediktor, kompleksitas pohon, dan mekanisme pemisahan. Pengaturan parameter perlu disesuaikan dengan tujuan pemodelan dan karakteristik data (Fransiska, 2026; Wright & Ziegler, 2017).
Beberapa parameter penting dalam Random Forest adalah sebagai berikut.
| Parameter | Penjelasan |
|---|---|
num.trees |
Jumlah pohon keputusan yang dibangun dalam ensemble. |
mtry |
Jumlah prediktor yang dipertimbangkan secara acak pada setiap node. |
min.node.size |
Ukuran minimum node yang mengatur pembentukan dan kompleksitas pohon. |
replace |
Menentukan apakah pengambilan sampel dilakukan dengan pengembalian. |
sample.fraction |
Proporsi observasi yang digunakan dalam pengambilan sampel. |
splitrule |
Menentukan kriteria pemisahan, misalnya Gini untuk klasifikasi atau variansi untuk regresi pada implementasi yang mendukungnya. |
importance |
Menentukan metode perhitungan kepentingan prediktor. |
Parameter yang tersedia dapat berbeda menurut perangkat lunak dan implementasi algoritma yang digunakan. Oleh karena itu, definisi parameter perlu disesuaikan dengan dokumentasi perangkat lunak terkait.
Pada DRF, selain parameter umum seperti jumlah pohon dan pemilihan prediktor, mekanisme pengambilan sampel pada setiap node menjadi karakteristik penting. Parameter khusus DRF perlu mengikuti implementasi yang digunakan karena tidak semua perangkat lunak menyediakan algoritma tersebut secara langsung.
Penentuan parameter yang tepat dapat dilakukan melalui validasi silang atau pendekatan evaluasi lain yang sesuai. Data uji sebaiknya tidak digunakan berulang kali untuk memilih parameter karena dapat menyebabkan penilaian kinerja model menjadi terlalu optimistis.
Evaluasi model dilakukan untuk mengetahui kemampuan metode dalam menghasilkan prediksi yang mendekati nilai aktual. Metrik evaluasi harus disesuaikan dengan jenis variabel respons, sehingga evaluasi klasifikasi tidak disamakan dengan evaluasi regresi.
Evaluasi klasifikasi dapat menggunakan confusion matrix yang memuat empat komponen utama, yaitu True Positive (TP), True Negative (TN), False Positive (FP), dan False Negative (FN).
Ukuran evaluasi klasifikasi meliputi:
** Accuracy **
Mengukur proporsi seluruh observasi yang diklasifikasikan dengan benar.
\[ Accuracy = \frac{TP+TN}{TP+TN+FP+FN}. \]
** Precision **
Mengukur proporsi prediksi positif yang benar-benar positif.
\[ Precision = \frac{TP}{TP+FP}. \]
** Recall atau Sensitivity **
Mengukur proporsi observasi positif aktual yang berhasil dikenali model.
\[ Recall = \frac{TP}{TP+FN}. \]
** Specificity **
Mengukur proporsi observasi negatif aktual yang berhasil dikenali model.
\[ Specificity = \frac{TN}{TN+FP}. \]
F1-score
Merupakan rata-rata harmonik antara precision dan recall.
\[ F1 = 2\frac{Precision\times Recall} {Precision+Recall}. \]
Nilai accuracy yang tinggi belum tentu menunjukkan model yang baik apabila kelas dalam data tidak seimbang. Oleh karena itu, precision, recall, dan F1-score perlu dipertimbangkan sesuai tujuan analisis.
Evaluasi regresi dilakukan dengan membandingkan nilai prediksi dan nilai aktual. Misalkan \(y_i\) adalah nilai aktual, \(\hat{y}_i\) adalah nilai prediksi, dan \(n\) adalah jumlah observasi yang dievaluasi.
** Mean Squared Error (MSE)**
MSE merupakan rata-rata kuadrat selisih antara nilai aktual dan nilai prediksi.
\[ MSE = \frac{1}{n} \sum_{i=1}^{n} (y_i-\hat{y}_i)^2. \]
** Root Mean Squared Error (RMSE)**
RMSE merupakan akar kuadrat MSE dan memiliki satuan yang sama dengan variabel respons.
\[ RMSE = \sqrt{ \frac{1}{n} \sum_{i=1}^{n} (y_i-\hat{y}_i)^2 }. \]
** Mean Absolute Error (MAE)**
MAE merupakan rata-rata nilai absolut selisih antara nilai aktual dan nilai prediksi.
\[ MAE = \frac{1}{n} \sum_{i=1}^{n} |y_i-\hat{y}_i|. \]
Koefisien Determinasi (\(R^2\))
Koefisien determinasi mengukur perbandingan variasi yang dapat dijelaskan oleh prediksi model terhadap variasi total data aktual. Salah satu bentuk perhitungannya adalah
\[ R^2 = 1- \frac{ \sum_{i=1}^{n}(y_i-\hat{y}_i)^2 }{ \sum_{i=1}^{n}(y_i-\bar{y})^2 }, \]
dengan \(\bar{y}\) merupakan rata-rata nilai aktual.
Nilai MSE, RMSE, dan MAE yang lebih kecil menunjukkan kesalahan prediksi yang lebih rendah. Nilai \(R^2\) yang lebih tinggi menunjukkan hasil yang lebih baik menurut ukuran tersebut, tetapi interpretasinya perlu mempertimbangkan data yang digunakan untuk evaluasi dan kesesuaian model.
Jenis data yang digunakan pada penelitian ini adalah data kategorik
dengan variabel respon berupa kelas diagnosis tumor. Data yang digunakan
merupakan dataset BreastCancer dari package
mlbench pada R. Sumber data yang digunakan dalam penelitian
ini yaitu data sekunder yang tersedia secara terbuka melalui
package tersebut, terdiri atas 699 observasi dan 11 variabel.
Setelah observasi yang mengandung missing value dihapus, data
yang dianalisis berjumlah 683 observasi.
Penelitian ini menggunakan sepuluh variabel, yaitu satu variabel respon dan sembilan variabel prediktor. Variabel Class adalah variabel respon yang menunjukkan jenis tumor, yaitu benign (jinak) dan malignant (ganas), dengan malignant ditetapkan sebagai kelas positif. Sembilan variabel prediktor berupa hasil penilaian karakteristik sel pada skala 1 sampai 10, yaitu Cl.thickness (ketebalan gumpalan sel), Cell.size (keseragaman ukuran sel), Cell.shape (keseragaman bentuk sel), Marg.adhesion (perlekatan tepi), Epith.c.size (ukuran sel epitel tunggal), Bare.nuclei (inti sel telanjang), Bl.cromatin (kromatin yang halus), Normal.nucleoli (nukleolus normal), dan Mitoses (jumlah mitosis). Variabel Id tidak digunakan dalam pemodelan karena hanya berfungsi sebagai pengenal pasien.
Berikut adalah langkah-langkah yang dilakukan dalam analisis:
ranger, mlbench, caret,
randomForest, dan pkgbuild.set.seed(234).num.trees
= 500, mtry = 3), lalu melihat OOB error, prediksi
data testing, confusion matrix, ukuran evaluasi, dan
variable importance.mtry = 2, 3, 5
dan num.trees = 200, 500, 1000.ntree = 500, mtry = 3) pada data
training dan testing yang sama.mtry dan ntree yang sama dengan
Random Forest.library(ranger)
library(mlbench)
library(caret)
# Memuat data
data(BreastCancer, package = "mlbench")
bc <- BreastCancer
# Melihat struktur dan ringkasan data
str(bc)## 'data.frame': 699 obs. of 11 variables:
## $ Id : chr "1000025" "1002945" "1015425" "1016277" ...
## $ Cl.thickness : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 5 5 3 6 4 8 1 2 2 4 ...
## $ Cell.size : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 4 1 8 1 10 1 1 1 2 ...
## $ Cell.shape : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 4 1 8 1 10 1 2 1 1 ...
## $ Marg.adhesion : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 5 1 1 3 8 1 1 1 1 ...
## $ Epith.c.size : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 2 7 2 3 2 7 2 2 2 2 ...
## $ Bare.nuclei : Factor w/ 10 levels "1","2","3","4",..: 1 10 2 4 1 10 10 1 1 1 ...
## $ Bl.cromatin : Factor w/ 10 levels "1","2","3","4",..: 3 3 3 3 3 9 3 3 1 2 ...
## $ Normal.nucleoli: Factor w/ 10 levels "1","2","3","4",..: 1 2 1 7 1 7 1 1 1 1 ...
## $ Mitoses : Factor w/ 9 levels "1","2","3","4",..: 1 1 1 1 1 1 1 1 5 1 ...
## $ Class : Factor w/ 2 levels "benign","malignant": 1 1 1 1 1 2 1 1 1 1 ...
## Id Cl.thickness Cell.size Cell.shape Marg.adhesion
## Length :699 1 :145 1 :384 1 :353 1 :407
## N.unique :645 5 :130 10 : 67 2 : 59 2 : 58
## N.blank : 0 3 :108 3 : 52 10 : 58 3 : 58
## Min.nchar: 5 4 : 80 2 : 45 3 : 56 10 : 55
## Max.nchar: 8 10 : 69 4 : 40 4 : 44 4 : 33
## 2 : 50 5 : 30 5 : 34 8 : 25
## (Other):117 (Other): 81 (Other): 95 (Other): 63
## Epith.c.size Bare.nuclei Bl.cromatin Normal.nucleoli Mitoses
## 2 :386 1 :402 2 :166 1 :443 1 :579
## 3 : 72 10 :132 3 :165 10 : 61 2 : 35
## 4 : 48 2 : 30 1 :152 3 : 44 3 : 33
## 1 : 47 5 : 30 7 : 73 2 : 36 10 : 14
## 6 : 41 3 : 28 4 : 40 8 : 24 4 : 12
## 5 : 39 (Other): 61 5 : 34 6 : 22 7 : 9
## (Other): 66 NAs : 16 (Other): 69 (Other): 69 (Other): 17
## Class
## benign :458
## malignant:241
##
##
##
##
##
## Id Cl.thickness Cell.size Cell.shape Marg.adhesion
## 0 0 0 0 0
## Epith.c.size Bare.nuclei Bl.cromatin Normal.nucleoli Mitoses
## 0 16 0 0 0
## Class
## 0
Berdasarkan hasil pemeriksaan, dataset BreastCancer terdiri atas 699 observasi dan 11 variabel. Variabel Id berisi nomor pengenal pasien, sembilan variabel prediktor berupa penilaian karakteristik sel berskala 1 sampai 10 yang tersimpan sebagai factor, dan variabel Class menjadi variabel respon. Pemeriksaan missing value menunjukkan bahwa hanya variabel Bare.nuclei yang memiliki data hilang, yaitu sebanyak 16 pengamatan. Karena jumlahnya kecil dibandingkan keseluruhan data, pengamatan tersebut dihapus pada tahap persiapan berikutnya.
# Menghapus Id karena hanya merupakan identifier
bc$Id <- NULL
# Menghapus observasi yang mengandung missing value
bc <- na.omit(bc)
# Memastikan respons berupa factor
bc$Class <- droplevels(as.factor(bc$Class))
levels(bc$Class)## [1] "benign" "malignant"
##
## benign malignant
## 444 239
##
## benign malignant
## 0.6500732 0.3499268
Setelah variabel Id dihapus dan 16 observasi yang mengandung missing value dibuang, data yang tersisa berjumlah 683 observasi dengan 10 variabel. Variabel Class memiliki dua kategori, yaitu benign dan malignant, dengan 444 pengamatan benign (65,01%) dan 239 pengamatan malignant (34,99%). Kelas benign memang lebih banyak, tetapi perbedaannya belum terlalu ekstrem sehingga data masih dapat langsung dimodelkan tanpa penanganan khusus.
# Membagi data training dan testing (70% training dan 30% testing)
set.seed(234)
idx.cls <- createDataPartition(bc$Class, p = 0.70, list = FALSE)
bc.train <- bc[idx.cls, ]
bc.test <- bc[-idx.cls, ]
dim(bc.train)## [1] 479 10
## [1] 204 10
##
## benign malignant
## 311 168
##
## benign malignant
## 133 71
##
## benign malignant
## 0.6492693 0.3507307
##
## benign malignant
## 0.6519608 0.3480392
Data dibagi menjadi 479 observasi sebagai data training dan
204 observasi sebagai data testing (sekitar 70% dan 30%). Data
training berisi 311 pengamatan benign dan 168
pengamatan malignant, sedangkan data testing berisi
133 pengamatan benign dan 71 pengamatan malignant.
Proporsi kelas pada kedua data hampir sama dengan data awal (sekitar 65%
benign dan 35% malignant) karena
createDataPartition() membagi data secara
stratified berdasarkan kelas, sehingga data testing
cukup mewakili kondisi data keseluruhan.
num.trees = 500
dan mtry = 3)# Membentuk Random Forest
set.seed(234)
rf.cls <- ranger(
Class ~ ., data = bc.train,
num.trees = 500,
mtry = 3,
min.node.size = 1,
replace = TRUE,
sample.fraction = 1,
importance = "permutation",
seed = 234
)
rf.cls## Ranger result
##
## Call:
## ranger(Class ~ ., data = bc.train, num.trees = 500, mtry = 3, min.node.size = 1, replace = TRUE, sample.fraction = 1, importance = "permutation", seed = 234)
##
## Type: Classification
## Number of trees: 500
## Sample size: 479
## Number of independent variables: 9
## Mtry: 3
## Target node size: 1
## Variable importance mode: permutation
## Splitrule: gini
## OOB prediction error: 2.92 %
## [1] 0.02922756
## predicted
## true benign malignant
## benign 303 8
## malignant 6 162
## [1] benign benign malignant malignant benign benign
## Levels: benign malignant
Model Random Forest dibentuk dari 500 pohon klasifikasi
dengan mtry = 3 dari 9 prediktor,
min.node.size = 1 sehingga pohon tumbuh sangat dalam, dan
importance = "permutation" supaya variable
importance dapat dihitung. Nilai OOB prediction error
sebesar 0,0292 berarti sekitar 2,92% observasi data training
salah diklasifikasikan ketika diprediksi hanya oleh pohon yang tidak
memuat observasi tersebut dalam sampel bootstrap-nya.
Confusion matrix OOB menunjukkan 303 pengamatan benign
terprediksi benar dan 8 salah, serta 162 pengamatan malignant
terprediksi benar dan 6 salah, sehingga total 14 dari 479 observasi yang
salah. Karena prediksi OOB tidak memakai pohon yang melihat observasi
tersebut, nilai ini lebih jujur dibandingkan error pada data
latih biasa.
# Prediksi data testing
pred.rf.cls <- predict(rf.cls, data = bc.test)$predictions
pred.rf.cls <- factor(pred.rf.cls, levels = levels(bc.test$Class))
head(pred.rf.cls)## [1] malignant benign benign benign benign malignant
## Levels: benign malignant
## Aktual Prediksi
## 1 benign malignant
## 2 benign benign
## 3 benign benign
## 4 benign benign
## 5 benign benign
## 6 malignant malignant
## 7 benign benign
## 8 benign benign
## 9 benign benign
## 10 malignant malignant
# Confusion Matrix dan ukuran evaluasi
cm.rf <- confusionMatrix(data = pred.rf.cls, reference = bc.test$Class,
positive = "malignant")
cm.rf## Confusion Matrix and Statistics
##
## Reference
## Prediction benign malignant
## benign 130 3
## malignant 3 68
##
## Accuracy : 0.9706
## 95% CI : (0.9371, 0.9891)
## No Information Rate : 0.652
## P-Value [Acc > NIR] : <2e-16
##
## Kappa : 0.9352
##
## Mcnemar's Test P-Value : 1
##
## Sensitivity : 0.9577
## Specificity : 0.9774
## Pos Pred Value : 0.9577
## Neg Pred Value : 0.9774
## Prevalence : 0.3480
## Detection Rate : 0.3333
## Detection Prevalence : 0.3480
## Balanced Accuracy : 0.9676
##
## 'Positive' Class : malignant
##
## Prediksi
## Aktual benign malignant
## benign 130 3
## malignant 3 68
TN <- tab.rf["benign", "benign"]
FP <- tab.rf["benign", "malignant"]
FN <- tab.rf["malignant", "benign"]
TP <- tab.rf["malignant", "malignant"]
Accuracy <- (TP + TN) / (TP + TN + FP + FN)
Precision <- TP / (TP + FP)
Recall <- TP / (TP + FN)
Specificity <- TN / (TN + FP)
F1 <- 2 * (Precision * Recall) / (Precision + Recall)
evaluasi.rf <- data.frame(Accuracy, Precision, Recall, Specificity,
F1_Score = F1)
round(evaluasi.rf, 4)## Accuracy Precision Recall Specificity F1_Score
## 1 0.9706 0.9577 0.9577 0.9774 0.9577
Pada data testing, model dasar memprediksi 130 dari 133 pengamatan benign dengan benar (TN) dan 3 salah (FP), serta 68 dari 71 pengamatan malignant dengan benar (TP) dan 3 salah (FN). Dari angka tersebut diperoleh accuracy sebesar 0,9706, precision sebesar 0,9577, recall sebesar 0,9577, specificity sebesar 0,9774, dan F1-score sebesar 0,9577. Error pada data testing (6 dari 204, yaitu 0,0294) sangat dekat dengan OOB error (0,0292), yang menunjukkan bahwa OOB error memang dapat dipakai sebagai perkiraan kemampuan model pada data baru. Pada model ini jumlah pasien ganas yang terlewat (FN) sama dengan jumlah pasien jinak yang diprediksi ganas (FP), sehingga precision dan recall bernilai sama.
## Bare.nuclei Cell.size Cell.shape Cl.thickness Marg.adhesion
## 0.0991 0.0853 0.0651 0.0260 0.0193
## Bl.cromatin Normal.nucleoli Epith.c.size Mitoses
## 0.0188 0.0148 0.0109 0.0032
barplot(importance.rf, las = 2,
main = "Variable Importance - Random Forest Classification",
ylab = "Permutation Importance")Berdasarkan permutation importance, variabel yang paling berpengaruh adalah Bare.nuclei (0,0991), diikuti Cell.size (0,0853) dan Cell.shape (0,0651), sedangkan Cl.thickness berada jauh di bawahnya (0,0260). Variabel dengan pengaruh paling kecil adalah Mitoses (0,0032). Artinya, jika nilai Bare.nuclei diacak, akurasi prediksi OOB menurun paling besar dibanding variabel lain, sehingga variabel ini paling dibutuhkan model untuk membedakan tumor jinak dan ganas. Sebaliknya, Mitoses hampir tidak berkontribusi pada ketepatan prediksi.
mtry = 2, 3, 5 dan
num.trees = 200, 500, 1000## mtry num.trees
## 1 2 200
## 2 3 200
## 3 5 200
## 4 2 500
## 5 3 500
## 6 5 500
## 7 2 1000
## 8 3 1000
## 9 5 1000
model.list <- list()
hasil.grid <- data.frame()
for (i in seq_len(nrow(grid))) {
mt <- grid$mtry[i]
nt <- grid$num.trees[i]
set.seed(234)
rf <- ranger(Class ~ ., data = bc.train,
num.trees = nt, mtry = mt, min.node.size = 1,
replace = TRUE, sample.fraction = 1,
importance = "permutation", seed = 234)
pred <- factor(predict(rf, data = bc.test)$predictions,
levels = levels(bc.test$Class))
tab <- table(Aktual = bc.test$Class, Prediksi = pred)
TN <- tab["benign", "benign"]; FP <- tab["benign", "malignant"]
FN <- tab["malignant", "benign"]; TP <- tab["malignant", "malignant"]
Pre <- TP / (TP + FP); Rec <- TP / (TP + FN)
hasil.grid <- rbind(hasil.grid, data.frame(
num.trees = nt, mtry = mt, OOB_Error = rf$prediction.error,
TN = TN, FP = FP, FN = FN, TP = TP,
Accuracy = (TP + TN) / sum(tab), Precision = Pre, Recall = Rec,
Specificity = TN / (TN + FP), F1_Score = 2 * Pre * Rec / (Pre + Rec)))
model.list[[paste0("nt", nt, "_mt", mt)]] <- rf
}
hasil.grid[, -c(1, 2, 4:7)] <- round(hasil.grid[, -c(1, 2, 4:7)], 4)
hasil.grid## num.trees mtry OOB_Error TN FP FN TP Accuracy Precision Recall Specificity
## 1 200 2 0.0292 130 3 1 70 0.9804 0.9589 0.9859 0.9774
## 2 200 3 0.0271 130 3 3 68 0.9706 0.9577 0.9577 0.9774
## 3 200 5 0.0292 129 4 4 67 0.9608 0.9437 0.9437 0.9699
## 4 500 2 0.0292 129 4 1 70 0.9755 0.9459 0.9859 0.9699
## 5 500 3 0.0292 130 3 3 68 0.9706 0.9577 0.9577 0.9774
## 6 500 5 0.0292 129 4 4 67 0.9608 0.9437 0.9437 0.9699
## 7 1000 2 0.0292 129 4 2 69 0.9706 0.9452 0.9718 0.9699
## 8 1000 3 0.0271 129 4 3 68 0.9657 0.9444 0.9577 0.9699
## 9 1000 5 0.0271 129 4 5 66 0.9559 0.9429 0.9296 0.9699
## F1_Score
## 1 0.9722
## 2 0.9577
## 3 0.9437
## 4 0.9655
## 5 0.9577
## 6 0.9437
## 7 0.9583
## 8 0.9510
## 9 0.9362
Kesembilan model dibentuk dengan pengaturan lain yang sama
(min.node.size = 1, replace = TRUE,
sample.fraction = 1) sehingga perbedaan hasil benar-benar
berasal dari mtry dan num.trees. Tabel di atas
memuat OOB error, empat komponen confusion matrix, dan
lima ukuran evaluasi pada data testing untuk setiap kombinasi.
Seluruh model menghasilkan accuracy di atas 0,95, dengan
kisaran 0,9559 sampai 0,9804, dan OOB error hanya berkisar
antara 0,0271 sampai 0,0292. Hal ini menunjukkan bahwa Random
Forest sudah bekerja sangat baik pada data BreastCancer
untuk semua kombinasi parameter yang dicoba.
mtry dan num.trees terhadap
Performa# Ringkasan rata-rata accuracy menurut mtry dan num.trees
aggregate(Accuracy ~ mtry, data = hasil.grid, FUN = mean)## mtry Accuracy
## 1 2 0.9755000
## 2 3 0.9689667
## 3 5 0.9591667
## num.trees Accuracy
## 1 200 0.9706000
## 2 500 0.9689667
## 3 1000 0.9640667
# Plot accuracy dan OOB error
warna <- c("blue", "red", "forestgreen")
par(mfrow = c(1, 2))
plot(NULL, xlim = c(200, 1000), ylim = range(hasil.grid$Accuracy),
xaxt = "n", xlab = "num.trees", ylab = "Accuracy (testing)",
main = "Accuracy Data Testing")
axis(1, at = c(200, 500, 1000))
for (k in 1:3) {
d <- subset(hasil.grid, mtry == c(2, 3, 5)[k])
lines(d$num.trees, d$Accuracy, type = "b", pch = 19, lwd = 2, col = warna[k])
}
legend("topright", legend = paste("mtry =", c(2, 3, 5)), col = warna,
pch = 19, lwd = 2, cex = 0.8)
plot(NULL, xlim = c(200, 1000), ylim = range(hasil.grid$OOB_Error),
xaxt = "n", xlab = "num.trees", ylab = "OOB Error", main = "OOB Error")
axis(1, at = c(200, 500, 1000))
for (k in 1:3) {
d <- subset(hasil.grid, mtry == c(2, 3, 5)[k])
lines(d$num.trees, d$OOB_Error, type = "b", pch = 19, lwd = 2, col = warna[k])
}Dari sisi mtry, rata-rata accuracy data
testing menurun seiring bertambahnya mtry, yaitu
sekitar 0,9755 untuk mtry = 2, 0,9690 untuk
mtry = 3, dan 0,9592 untuk mtry = 5. Pola ini
sesuai dengan teori: semakin besar mtry, semakin banyak
prediktor kandidat di setiap node, sehingga prediktor yang kuat
lebih sering terpilih dan pohon-pohon menjadi lebih mirip (korelasi
antar-pohon naik). Akibatnya, manfaat pengacakan prediktor berkurang.
Pada mtry = 5, jumlah kesalahan testing bahkan
mencapai 8 sampai 9 pengamatan, dibanding 4 sampai 6 pengamatan pada
mtry = 2.
Dari sisi num.trees, menambah pohon dari 200 menjadi 500
atau 1000 tidak memperbaiki akurasi. Untuk mtry = 2
misalnya, accuracy justru turun dari 0,9804 (200 pohon) menjadi
0,9755 (500 pohon) dan 0,9706 (1000 pohon), sedangkan untuk
mtry = 3 dan 5 hasilnya hampir tidak berubah. Perubahan
sebesar satu atau dua pengamatan dari 204 data lebih mungkin berasal
dari variasi acak pada pohon-pohon yang terbentuk daripada pengaruh
sebenarnya dari jumlah pohon. Hal ini wajar karena Random
Forest tidak mudah overfitting ketika jumlah pohon
ditambah; jumlah pohon yang lebih banyak terutama membuat prediksi lebih
stabil, dan pada data sekecil ini kestabilan tersebut sudah tercapai
sekitar 200 pohon. Probst dan Boulesteix (2018) juga menunjukkan bahwa
error klasifikasi dapat mencapai nilai terendah lalu naik lagi
saat pohon ditambah, sehingga turunnya accuracy pada 500 dan
1000 pohon di sini masih dalam kewajaran.
OOB error berada pada 0,0292 untuk sebagian besar model, dan
sedikit lebih rendah (0,0271) pada tiga model, yaitu
(num.trees = 200, mtry = 3), (1000, 3), dan
(1000, 5). Selisih 0,0271 dengan 0,0292 setara dengan hanya satu
observasi dari 479 data training, sehingga OOB error
tidak cukup membedakan model-model ini. Menariknya, OOB error
terendah tidak selalu sejalan dengan accuracy tertinggi pada
data testing, misalnya model (1000, 5) memiliki OOB
error terendah tetapi accuracy testing
terendah (0,9559).
# Matriks variable importance seluruh model
nama.var <- names(model.list[[1]]$variable.importance)
imp.mat <- sapply(model.list, function(m) m$variable.importance[nama.var])
round(imp.mat, 4)## nt200_mt2 nt200_mt3 nt200_mt5 nt500_mt2 nt500_mt3 nt500_mt5
## Cl.thickness 0.0245 0.0278 0.0233 0.0267 0.0260 0.0217
## Cell.size 0.0787 0.0917 0.0898 0.0773 0.0853 0.0909
## Cell.shape 0.0610 0.0628 0.0427 0.0630 0.0651 0.0460
## Marg.adhesion 0.0193 0.0191 0.0209 0.0191 0.0193 0.0195
## Epith.c.size 0.0117 0.0099 0.0086 0.0132 0.0109 0.0093
## Bare.nuclei 0.0839 0.0972 0.1259 0.0805 0.0991 0.1243
## Bl.cromatin 0.0215 0.0180 0.0142 0.0219 0.0188 0.0153
## Normal.nucleoli 0.0204 0.0160 0.0217 0.0218 0.0148 0.0203
## Mitoses 0.0038 0.0037 0.0026 0.0031 0.0032 0.0030
## nt1000_mt2 nt1000_mt3 nt1000_mt5
## Cl.thickness 0.0263 0.0248 0.0227
## Cell.size 0.0779 0.0855 0.0911
## Cell.shape 0.0626 0.0593 0.0438
## Marg.adhesion 0.0189 0.0195 0.0189
## Epith.c.size 0.0140 0.0119 0.0097
## Bare.nuclei 0.0817 0.1003 0.1214
## Bl.cromatin 0.0205 0.0192 0.0162
## Normal.nucleoli 0.0199 0.0165 0.0208
## Mitoses 0.0033 0.0032 0.0032
# Plot variable importance untuk mtry = 2, 3, 5 pada num.trees = 500
par(mfrow = c(1, 3))
for (k in c("nt500_mt2", "nt500_mt3", "nt500_mt5")) {
imp <- sort(model.list[[k]]$variable.importance, decreasing = TRUE)
barplot(imp, las = 2, main = k, ylab = "Permutation Importance",
cex.names = 0.7)
}Pada seluruh sembilan model, tiga variabel teratas selalu sama, yaitu
Bare.nuclei, Cell.size, dan Cell.shape,
disusul Cl.thickness di urutan keempat, sedangkan
Mitoses selalu menjadi variabel dengan importance
terendah (sekitar 0,0026 sampai 0,0038). Dengan kata lain, kesimpulan
tentang variabel penting sangat konsisten dan tidak terpengaruh oleh
pilihan mtry maupun num.trees. Meski begitu,
besar importance bergeser mengikuti mtry:
Bare.nuclei naik dari sekitar 0,081 (mtry = 2)
menjadi 0,099 (mtry = 3) dan 0,124 (mtry = 5),
sebaliknya Cell.shape turun dari sekitar 0,063 menjadi 0,046.
Hal ini masuk akal karena saat mtry besar, variabel terkuat
seperti Bare.nuclei lebih sering tersedia di setiap
node dan mengambil porsi split yang sebelumnya dibagi
ke variabel lain yang berkorelasi dengannya, seperti
Cell.shape. Pada mtry kecil, importance
menjadi lebih merata di antara variabel-variabel yang saling
berkorelasi.
library(randomForest)
library(pkgbuild)
# Cek compiler
pkgbuild::check_build_tools(debug = FALSE)
cat("R version :", R.version.string, "\n")## R version : R version 4.6.1 (2026-06-24 ucrt)
## make : C:\rtools45\usr\bin\make.exe
## gcc : C:\rtools45\X86_64~1.POS\bin\gcc.exe
## gfortran : C:\rtools45\X86_64~1.POS\bin\gfortran.exe
if (Sys.which("make") == "" || Sys.which("gcc") == "" ||
Sys.which("gfortran") == "") {
stop("Rtools/compiler belum tersedia. Install Rtools lalu restart RStudio.")
}
# Penyesuaian lintas sistem operasi
os.win <- .Platform$OS.type == "windows"
nama.lib <- paste0("DRF", .Platform$dynlib.ext)
r.bin <- file.path(R.home("bin"), if (os.win) "R.exe" else "R")
# Download dan ekstrak source code DRF
drf.extract <- tempfile(pattern = "DRF_extract_"); dir.create(drf.extract, recursive = TRUE)
src.build <- tempfile(pattern = "DRF_build_"); dir.create(src.build, recursive = TRUE)
drf.zip <- tempfile(fileext = ".zip")
download.file(url = "https://raw.githubusercontent.com/shan-stat/DRF/master/DRF.zip",
destfile = drf.zip, mode = "wb")
unzip(drf.zip, exdir = drf.extract)
classTree.file <- list.files(drf.extract, pattern = "^classTree\\.c$",
recursive = TRUE, full.names = TRUE)
if (length(classTree.file) == 0) stop("classTree.c tidak ditemukan dalam DRF.zip.")
src.asli <- dirname(classTree.file[1])
hasil.copy <- file.copy(from = list.files(src.asli, full.names = TRUE),
to = src.build, overwrite = TRUE)
if (!all(hasil.copy)) stop("Ada file source DRF yang gagal dicopy.")
file.wajib <- c("classTree.c", "regrf.c", "regTree.c", "rf.c", "rf.h",
"rfsub_drf.f", "rfutils.c")
cek.file <- file.exists(file.path(src.build, file.wajib))
names(cek.file) <- file.wajib
cek.file## classTree.c regrf.c regTree.c rf.c rf.h rfsub_drf.f
## TRUE TRUE TRUE TRUE TRUE TRUE
## rfutils.c
## TRUE
# Patch source DRF untuk R >= 4.5 (Calloc/Realloc/Free -> R_Calloc/R_Realloc/R_Free)
c.files <- list.files(src.build, pattern = "\\.(c|h)$", full.names = TRUE)
if (getRversion() >= "4.5.0") {
for (f in c.files) {
txt <- readLines(f, warn = FALSE)
txt <- gsub("\\bCalloc\\s*\\(", "R_Calloc(", txt, perl = TRUE)
txt <- gsub("\\bRealloc\\s*\\(", "R_Realloc(", txt, perl = TRUE)
txt <- gsub("\\bFree\\s*\\(", "R_Free(", txt, perl = TRUE)
writeLines(txt, f, useBytes = TRUE)
}
cat("Patch kompatibilitas R >= 4.5 selesai.\n")
} else {
cat("R < 4.5: patch tidak diperlukan.\n")
}## Patch kompatibilitas R >= 4.5 selesai.
# Compile source code menjadi library DRF
unlink(list.files(src.build, pattern = "\\.(o|dll|so)$", full.names = TRUE), force = TRUE)
wd.awal <- getwd()
setwd(src.build)
src.files <- c("classTree.c", "regrf.c", "regTree.c", "rf.c", "rfsub_drf.f", "rfutils.c")
status <- system2(command = r.bin, args = c("CMD", "SHLIB", "-o", nama.lib, src.files),
stdout = FALSE, stderr = FALSE)
setwd(wd.awal)
dll.path <- file.path(src.build, nama.lib)
cat("Status compile =", status, "| library tersedia =", file.exists(dll.path), "\n")## Status compile = 0 | library tersedia = TRUE
# Load native engine dan fungsi R resmi DRF
drf.dll <- dyn.load(dll.path)
DRF_classRF <- getNativeSymbolInfo("classRF", PACKAGE = drf.dll[["name"]])
DRF_regRF <- getNativeSymbolInfo("regRF", PACKAGE = drf.dll[["name"]])
func.file <- tempfile(fileext = ".R")
download.file(url = "https://raw.githubusercontent.com/shan-stat/DRF/master/FuncDRF.R",
destfile = func.file, mode = "wb")
kode <- readLines(func.file, warn = FALSE)
# Ambil hanya Step 1 sampai Step 3
i4 <- grep("^## Step 4\\.", kode)[1]
if (is.na(i4)) stop("Bagian Step 4 tidak ditemukan dalam FuncDRF.R.")
kode.drf <- kode[1:(i4 - 1)]
# Arahkan classRF dan regRF ke library DRF
kode.drf <- gsub('.C("classRF",', '.C(DRF_classRF,', kode.drf, fixed = TRUE)
kode.drf <- gsub('.C("regRF",', '.C(DRF_regRF,', kode.drf, fixed = TRUE)
eval(parse(text = paste(kode.drf, collapse = "\n")), envir = .GlobalEnv)
exists("DRF.default")## [1] TRUE
## [1] TRUE
Syntax tersebut memeriksa ketersediaan compiler,
mengunduh dan mengekstrak kode sumber DRF, melakukan patch
kompatibilitas hanya jika versi R sudah 4.5 atau lebih baru,
mengompilasi kode C dan Fortran menjadi satu library, lalu
memuatnya dan mengarahkan fungsi classRF dan
regRF ke library tersebut. Hasil pemeriksaan
TRUE pada DRF.default dan
DRF.formula menunjukkan fungsi DRF berhasil tersedia di R,
dan status kompilasi bernilai 0 menandakan proses kompilasi berjalan
tanpa kegagalan. Fungsi DRF.default() inilah yang dipakai
untuk membentuk model pada bagian berikutnya.
# Training
x.train <- bc.train[, names(bc.train) != "Class", drop = FALSE]
y.train <- bc.train$Class
# Testing
x.test <- bc.test[, names(bc.test) != "Class", drop = FALSE]
y.test <- bc.test$Class
dim(x.train)## [1] 479 9
## [1] 204 9
Berbeda dengan ranger() yang memakai rumus
Class ~ ., fungsi DRF.default() menerima
prediktor (x) dan respons (y) secara terpisah.
Data training terdiri atas 479 observasi dengan 9 prediktor,
dan data testing terdiri atas 204 observasi dengan 9 prediktor.
Data yang dipakai identik dengan data pada Random Forest karena
berasal dari bc.train dan bc.test yang sama,
sehingga perbandingan kedua metode dilakukan pada kondisi yang adil.
ntree = 500 dan mtry =
3)set.seed(234)
drf.cls <- DRF.default(
x = x.train, y = y.train,
xtest = x.test, ytest = y.test,
ntree = 500, mtry = 3, nodesize = 1,
importance = TRUE
)
print(drf.cls)##
## Call:
## randomForest(x = x.train, y = y.train, xtest = x.test, ytest = y.test, ntree = 500, mtry = 3, nodesize = 1, importance = TRUE)
## Type of random forest: classification
## Number of trees: 500
## No. of variables tried at each split: 3
##
## OOB estimate of error rate: NaN%
## Confusion matrix:
## benign malignant class.error
## benign 0 0 NaN
## malignant 0 0 NaN
## Test set error rate: 2.94%
## Confusion matrix:
## benign malignant class.error
## benign 129 4 0.03007519
## malignant 2 69 0.02816901
Model DRF dibentuk dengan pengaturan yang sama seperti Random
Forest dasar, yaitu 500 pohon, mtry = 3, dan
nodesize = 1, sehingga perbedaan hasil berasal dari
mekanisme pembentukan pohon. Output menunjukkan test set
error rate sebesar 2,94%, dengan 129 pengamatan benign dan
69 pengamatan malignant terprediksi benar. Hal yang perlu
diperhatikan adalah OOB estimate of error rate bernilai
NaN. Hal ini wajar dan bukan kesalahan: karena setiap pohon
DRF dibangun dari seluruh data training, tidak ada observasi
yang “tertinggal” (out-of-bag) untuk menghitung prediksi OOB.
Dengan demikian, OOB error hanya dapat digunakan sebagai
evaluasi internal pada Random Forest, sedangkan DRF harus
dievaluasi dengan data testing.
pred.drf.cls <- factor(drf.cls$test$predicted, levels = levels(y.test))
hasil.drf.cls <- data.frame(Aktual = y.test, Prediksi = pred.drf.cls)
head(hasil.drf.cls, 10)## Aktual Prediksi
## 2 benign malignant
## 5 benign benign
## 7 benign benign
## 9 benign benign
## 12 benign benign
## 19 malignant malignant
## 27 benign benign
## 30 benign benign
## 32 benign benign
## 37 malignant malignant
cm.drf.cls <- confusionMatrix(data = pred.drf.cls, reference = y.test,
positive = "malignant")
cm.drf.cls## Confusion Matrix and Statistics
##
## Reference
## Prediction benign malignant
## benign 129 2
## malignant 4 69
##
## Accuracy : 0.9706
## 95% CI : (0.9371, 0.9891)
## No Information Rate : 0.652
## P-Value [Acc > NIR] : <2e-16
##
## Kappa : 0.9356
##
## Mcnemar's Test P-Value : 0.6831
##
## Sensitivity : 0.9718
## Specificity : 0.9699
## Pos Pred Value : 0.9452
## Neg Pred Value : 0.9847
## Prevalence : 0.3480
## Detection Rate : 0.3382
## Detection Prevalence : 0.3578
## Balanced Accuracy : 0.9709
##
## 'Positive' Class : malignant
##
## Prediksi
## Aktual benign malignant
## benign 129 4
## malignant 2 69
TN <- tab.drf["benign", "benign"]; FP <- tab.drf["benign", "malignant"]
FN <- tab.drf["malignant", "benign"]; TP <- tab.drf["malignant", "malignant"]
Accuracy <- (TP + TN) / (TP + TN + FP + FN)
Precision <- TP / (TP + FP)
Recall <- TP / (TP + FN)
Specificity <- TN / (TN + FP)
F1 <- 2 * (Precision * Recall) / (Precision + Recall)
evaluasi.drf.cls <- data.frame(Accuracy, Precision, Recall, Specificity,
F1_Score = F1)
round(evaluasi.drf.cls, 4)## Accuracy Precision Recall Specificity F1_Score
## 1 0.9706 0.9452 0.9718 0.9699 0.9583
Pada data testing, DRF memprediksi 129 dari 133 pengamatan benign dengan benar (TN) dan 4 salah (FP), serta 69 dari 71 pengamatan malignant dengan benar (TP) dan 2 salah (FN). Dari angka tersebut diperoleh accuracy sebesar 0,9706, precision sebesar 0,9452, recall sebesar 0,9718, specificity sebesar 0,9699, dan F1-score sebesar 0,9583, dengan nilai Kappa sebesar 0,9356. Accuracy ini sama dengan Random Forest dasar (0,9706), tetapi susunan kesalahannya berbeda: DRF lebih jarang melewatkan pasien ganas (FN = 2 dibanding 3) namun lebih sering salah menandai pasien jinak sebagai ganas (FP = 4 dibanding 3).
## benign malignant MeanDecreaseAccuracy MeanDecreaseGini
## Cl.thickness 0 0 0 9.075173
## Cell.size 0 0 0 61.684494
## Cell.shape 0 0 0 47.177653
## Marg.adhesion 0 0 0 6.648088
## Epith.c.size 0 0 0 29.134315
## Bare.nuclei 0 0 0 37.881146
## Bl.cromatin 0 0 0 21.929163
## Normal.nucleoli 0 0 0 19.781594
## Mitoses 0 0 0 2.188849
# Mean Decrease Accuracy
imp.drf.acc <- sort(importance.drf.cls[, "MeanDecreaseAccuracy"], decreasing = TRUE)
imp.drf.acc## Cl.thickness Cell.size Cell.shape Marg.adhesion Epith.c.size
## 0 0 0 0 0
## Bare.nuclei Bl.cromatin Normal.nucleoli Mitoses
## 0 0 0 0
if (all(abs(imp.drf.acc) < .Machine$double.eps)) {
message("MeanDecreaseAccuracy bernilai 0 untuk seluruh prediktor; ",
"ukuran ini tidak informatif pada implementasi DRF yang digunakan.")
} else {
barplot(imp.drf.acc, las = 2, main = "Variable Importance DRF - Accuracy",
ylab = "Mean Decrease Accuracy")
}
# Mean Decrease Gini
imp.drf.gini <- sort(importance.drf.cls[, "MeanDecreaseGini"], decreasing = TRUE)
round(imp.drf.gini, 4)## Cell.size Cell.shape Bare.nuclei Epith.c.size Bl.cromatin
## 61.6845 47.1777 37.8811 29.1343 21.9292
## Normal.nucleoli Cl.thickness Marg.adhesion Mitoses
## 19.7816 9.0752 6.6481 2.1888
barplot(imp.drf.gini, las = 2, main = "Variable Importance DRF - Gini",
ylab = "Mean Decrease Gini")Nilai Mean Decrease Accuracy bernilai 0 untuk seluruh prediktor, sehingga ukuran ini tidak informatif pada implementasi DRF yang digunakan dan grafiknya tidak ditampilkan (sesuai pengaman pada sintaks). Penyebabnya sejalan dengan temuan sebelumnya: ukuran ini bergantung pada observasi OOB, sedangkan pada DRF tidak ada observasi OOB. Karena itu, variable importance DRF dibaca dari Mean Decrease Gini. Variabel paling berpengaruh adalah Cell.size (61,684), diikuti Cell.shape (47,178) dan Bare.nuclei (37,881), sedangkan Mitoses paling kecil (2,189). Perlu dicatat bahwa Gini importance dan permutation importance pada Random Forest memiliki skala dan cara hitung berbeda sehingga nilainya tidak dibandingkan secara langsung, hanya urutan variabelnya. Meski urutannya sedikit berbeda (Bare.nuclei di peringkat pertama pada Random Forest, Cell.size pada DRF), tiga variabel teratas dan variabel terendah (Mitoses) sama pada kedua metode.
mtry = 2, 3, 5 dan
ntree = 200, 500, 1000hasil.drf <- data.frame()
imp.drf.list <- list()
for (i in seq_len(nrow(grid))) {
mt <- grid$mtry[i]
nt <- grid$num.trees[i]
set.seed(234)
waktu <- system.time(
d <- DRF.default(x = x.train, y = y.train, xtest = x.test, ytest = y.test,
ntree = nt, mtry = mt, nodesize = 1, importance = TRUE)
)["elapsed"]
pred <- factor(d$test$predicted, levels = levels(y.test))
tab <- table(Aktual = y.test, Prediksi = pred)
TN <- tab["benign", "benign"]; FP <- tab["benign", "malignant"]
FN <- tab["malignant", "benign"]; TP <- tab["malignant", "malignant"]
Pre <- TP / (TP + FP); Rec <- TP / (TP + FN)
hasil.drf <- rbind(hasil.drf, data.frame(
ntree = nt, mtry = mt,
TN = TN, FP = FP, FN = FN, TP = TP,
Accuracy = (TP + TN) / sum(tab), Precision = Pre, Recall = Rec,
Specificity = TN / (TN + FP), F1_Score = 2 * Pre * Rec / (Pre + Rec),
Waktu_detik = as.numeric(waktu)))
imp.drf.list[[paste0("nt", nt, "_mt", mt)]] <- d$importance[, "MeanDecreaseGini"]
}
hasil.drf[, -c(1:6)] <- round(hasil.drf[, -c(1:6)], 4)
hasil.drf## ntree mtry TN FP FN TP Accuracy Precision Recall Specificity F1_Score
## 1 200 2 129 4 1 70 0.9755 0.9459 0.9859 0.9699 0.9655
## 2 200 3 129 4 2 69 0.9706 0.9452 0.9718 0.9699 0.9583
## 3 200 5 129 4 3 68 0.9657 0.9444 0.9577 0.9699 0.9510
## 4 500 2 129 4 0 71 0.9804 0.9467 1.0000 0.9699 0.9726
## 5 500 3 129 4 2 69 0.9706 0.9452 0.9718 0.9699 0.9583
## 6 500 5 129 4 2 69 0.9706 0.9452 0.9718 0.9699 0.9583
## 7 1000 2 129 4 0 71 0.9804 0.9467 1.0000 0.9699 0.9726
## 8 1000 3 129 4 1 70 0.9755 0.9459 0.9859 0.9699 0.9655
## 9 1000 5 129 4 2 69 0.9706 0.9452 0.9718 0.9699 0.9583
## Waktu_detik
## 1 0.86
## 2 0.75
## 3 1.03
## 4 1.45
## 5 1.83
## 6 2.16
## 7 3.19
## 8 3.00
## 9 3.73
# Rata-rata accuracy DRF menurut mtry dan ntree
aggregate(Accuracy ~ mtry, data = hasil.drf, FUN = mean)## mtry Accuracy
## 1 2 0.9787667
## 2 3 0.9722333
## 3 5 0.9689667
## ntree Accuracy
## 1 200 0.9706000
## 2 500 0.9738667
## 3 1000 0.9755000
# Urutan variabel menurut Gini importance
imp.drf.mat <- sapply(imp.drf.list, function(v) v[nama.var])
round(imp.drf.mat, 2)## nt200_mt2 nt200_mt3 nt200_mt5 nt500_mt2 nt500_mt3 nt500_mt5
## Cl.thickness 13.75 9.62 7.50 12.69 9.08 8.09
## Cell.size 55.76 61.94 87.92 54.94 61.68 90.21
## Cell.shape 39.09 50.15 45.30 39.48 47.18 45.28
## Marg.adhesion 14.15 8.11 4.81 13.92 6.65 4.87
## Epith.c.size 23.01 23.43 23.17 25.10 29.13 25.56
## Bare.nuclei 40.00 37.59 39.55 36.40 37.88 36.22
## Bl.cromatin 24.45 20.34 16.62 23.17 21.93 14.37
## Normal.nucleoli 21.66 21.02 13.39 24.83 19.78 13.65
## Mitoses 2.25 1.93 1.74 2.33 2.19 1.69
## nt1000_mt2 nt1000_mt3 nt1000_mt5
## Cl.thickness 13.29 9.30 8.03
## Cell.size 54.84 63.62 90.44
## Cell.shape 38.98 43.65 44.39
## Marg.adhesion 13.13 7.66 5.07
## Epith.c.size 24.86 29.52 26.06
## Bare.nuclei 36.98 38.50 35.31
## Bl.cromatin 24.40 21.44 14.32
## Normal.nucleoli 24.26 20.44 14.41
## Mitoses 2.46 2.04 1.68
Seperti pada Random Forest, kesembilan model DRF
menghasilkan kinerja yang sangat baik, yaitu accuracy antara
0,9657 sampai 0,9804. Ada dua pola yang menonjol. Pertama, jumlah
false positive selalu 4 pada seluruh model, sehingga
specificity konstan di 0,9699; dengan kata lain, empat pasien
jinak yang sama selalu diprediksi ganas, kemungkinan karena
karakteristik selnya mirip tumor ganas. Perbedaan antarmodel sepenuhnya
berasal dari false negative, yang bervariasi dari 0 sampai 3.
Kedua, mtry yang lebih kecil lebih baik: rata-rata
accuracy sekitar 0,9788 untuk mtry = 2, 0,9722
untuk mtry = 3, dan 0,9690 untuk mtry = 5,
dengan recall mencapai 1,0000 pada mtry = 2 saat
ntree = 500 dan 1000. Pola ini sama dengan Random
Forest.
Pada DRF, penambahan pohon justru sedikit membantu: rata-rata
accuracy naik dari sekitar 0,9706 (200 pohon) ke 0,9739 (500
pohon) dan 0,9755 (1000 pohon), berbeda dengan Random Forest
yang cenderung datar atau turun. Namun, selisihnya hanya satu atau dua
pengamatan dari 204 data testing, sehingga tidak boleh dianggap
perbedaan yang pasti. Waktu komputasi meningkat kira-kira sebanding
dengan jumlah pohon dan sedikit bertambah pada mtry yang
lebih besar. Gini importance pada seluruh model DRF konsisten
menempatkan Cell.size, Cell.shape, dan
Bare.nuclei sebagai tiga variabel teratas, serta
Mitoses paling rendah.
# Gabungkan hasil RF dan DRF untuk 9 kombinasi parameter
perbandingan <- data.frame(
ntree = hasil.grid$num.trees, mtry = hasil.grid$mtry,
Acc_RF = hasil.grid$Accuracy, Acc_DRF = hasil.drf$Accuracy,
Rec_RF = hasil.grid$Recall, Rec_DRF = hasil.drf$Recall,
Spe_RF = hasil.grid$Specificity, Spe_DRF = hasil.drf$Specificity,
F1_RF = hasil.grid$F1_Score, F1_DRF = hasil.drf$F1_Score)
perbandingan$Selisih_Acc <- round(perbandingan$Acc_DRF - perbandingan$Acc_RF, 4)
perbandingan## ntree mtry Acc_RF Acc_DRF Rec_RF Rec_DRF Spe_RF Spe_DRF F1_RF F1_DRF
## 1 200 2 0.9804 0.9755 0.9859 0.9859 0.9774 0.9699 0.9722 0.9655
## 2 200 3 0.9706 0.9706 0.9577 0.9718 0.9774 0.9699 0.9577 0.9583
## 3 200 5 0.9608 0.9657 0.9437 0.9577 0.9699 0.9699 0.9437 0.9510
## 4 500 2 0.9755 0.9804 0.9859 1.0000 0.9699 0.9699 0.9655 0.9726
## 5 500 3 0.9706 0.9706 0.9577 0.9718 0.9774 0.9699 0.9577 0.9583
## 6 500 5 0.9608 0.9706 0.9437 0.9718 0.9699 0.9699 0.9437 0.9583
## 7 1000 2 0.9706 0.9804 0.9718 1.0000 0.9699 0.9699 0.9583 0.9726
## 8 1000 3 0.9657 0.9755 0.9577 0.9859 0.9699 0.9699 0.9510 0.9655
## 9 1000 5 0.9559 0.9706 0.9296 0.9718 0.9699 0.9699 0.9362 0.9583
## Selisih_Acc
## 1 -0.0049
## 2 0.0000
## 3 0.0049
## 4 0.0049
## 5 0.0000
## 6 0.0098
## 7 0.0098
## 8 0.0098
## 9 0.0147
# Ringkasan: berapa kombinasi DRF lebih baik, sama, atau lebih buruk dari RF
table(factor(sign(perbandingan$Selisih_Acc), levels = c(1, 0, -1),
labels = c("DRF lebih baik", "Sama", "RF lebih baik")))##
## DRF lebih baik Sama RF lebih baik
## 6 2 1
# Rata-rata seluruh kombinasi
round(c(RF = mean(hasil.grid$Accuracy), DRF = mean(hasil.drf$Accuracy)), 4)## RF DRF
## 0.9679 0.9733
# Plot accuracy RF vs DRF
par(mfrow = c(1, 3))
for (mt in c(2, 3, 5)) {
a <- subset(perbandingan, mtry == mt)
plot(a$ntree, a$Acc_RF, type = "b", pch = 19, lwd = 2, col = "blue",
ylim = range(c(perbandingan$Acc_RF, perbandingan$Acc_DRF)),
xaxt = "n", xlab = "Jumlah pohon", ylab = "Accuracy (testing)",
main = paste("mtry =", mt))
axis(1, at = c(200, 500, 1000))
lines(a$ntree, a$Acc_DRF, type = "b", pch = 17, lwd = 2, col = "red")
legend("bottomright", legend = c("RF", "DRF"), col = c("blue", "red"),
pch = c(19, 17), lwd = 2, cex = 0.8)
}Dari sembilan kombinasi parameter, DRF memiliki accuracy
lebih tinggi pada enam kombinasi, sama pada dua kombinasi
(mtry = 3 dengan 200 dan 500 pohon), dan lebih rendah hanya
pada satu kombinasi (mtry = 2 dengan 200 pohon). Rata-rata
accuracy seluruh kombinasi adalah sekitar 0,9733 untuk DRF dan
0,9679 untuk Random Forest. Keunggulan DRF paling jelas pada
mtry = 5 dan jumlah pohon besar, misalnya pada 1000 pohon
dengan mtry = 5, DRF mencapai 0,9706 sedangkan Random
Forest 0,9559. Hal ini sejalan dengan gagasan Han et al. (2020)
bahwa pohon yang dibangun dari seluruh data dan bootstrap pada
setiap node dapat memberi prediksi lebih akurat ketika pohon
Random Forest belum cukup besar. Meski begitu, selisih terbesar
hanya sekitar 3 pengamatan dari 204, sehingga keunggulan DRF pada data
ini bersifat tipis dan belum tentu bermakna secara statistik.
Dari sisi jenis kesalahan, DRF cenderung lebih baik pada
recall (sampai 1,0000 pada mtry = 2), sedangkan
Random Forest cenderung lebih baik pada specificity
(0,9699 sampai 0,9774, dibanding DRF yang tetap 0,9699). Pada konteks
diagnosis, recall yang tinggi lebih diutamakan karena pasien
ganas yang terlewat berisiko lebih besar.
# RF (ranger dan randomForest) vs DRF pada ntree = 500, mtry = 3
set.seed(234)
t.rf <- system.time(
rf.ranger <- ranger(Class ~ ., data = bc.train, num.trees = 500, mtry = 3,
min.node.size = 1, seed = 234))["elapsed"]
set.seed(234)
t.rfo <- system.time(
rf.pkg <- randomForest::randomForest(x.train, y.train, ntree = 500, mtry = 3,
nodesize = 1, keep.forest = TRUE))["elapsed"]
set.seed(234)
t.drf <- system.time(
drf.pohon <- DRF.default(x = x.train, y = y.train, ntree = 500, mtry = 3,
nodesize = 1, keep.forest = TRUE))["elapsed"]
data.frame(
Metode = c("RF (ranger)", "RF (randomForest)", "DRF"),
Waktu_detik = round(c(t.rf, t.rfo, t.drf), 3),
Rata2_node_per_pohon = c(NA, round(mean(rf.pkg$forest$ndbigtree), 1),
round(mean(drf.pohon$forest$ndbigtree), 1)),
OOB_Error = c(round(rf.ranger$prediction.error, 4),
round(rf.pkg$err.rate[500, "OOB"], 4), NA))## Metode Waktu_detik Rata2_node_per_pohon OOB_Error
## 1 RF (ranger) 0.05 NA 0.0292
## 2 RF (randomForest) 0.65 38.6 0.0271
## 3 DRF 1.56 61.1 NA
Perbandingan ukuran pohon dilakukan dengan
randomForest::randomForest() karena memakai mesin yang sama
dengan DRF, sehingga selisihnya benar-benar mencerminkan perbedaan
algoritma, bukan perbedaan perangkat lunak. Rata-rata jumlah
node per pohon pada DRF (sekitar 61) jauh lebih besar dibanding
Random Forest (sekitar 39). Temuan ini mendukung teori bahwa
DRF menghasilkan pohon yang lebih besar karena setiap pohon tumbuh dari
seluruh data training. Dari sisi waktu komputasi, DRF lebih
lama dibanding randomForest pada pengaturan yang sama
(kira-kira 1,5 sampai 2 kali pada percobaan ini, dengan angka pasti
bergantung pada komputer), sedangkan ranger jauh lebih
cepat karena diimplementasikan dalam C++ dan bukan karena algoritmanya
berbeda. Untuk OOB error, Random Forest menghasilkan
nilai sekitar 0,027 sampai 0,029, sedangkan DRF tidak memiliki nilai OOB
(NaN) sebagaimana dijelaskan sebelumnya. Jadi, jika
evaluasi internal tanpa data testing diperlukan, Random
Forest memiliki keunggulan praktis.
# Gabungkan semua model (RF dan DRF) dan urutkan berdasarkan accuracy, lalu F1
semua <- rbind(
data.frame(Metode = "RF", ntree = hasil.grid$num.trees, mtry = hasil.grid$mtry,
FP = hasil.grid$FP, FN = hasil.grid$FN, Accuracy = hasil.grid$Accuracy,
Precision = hasil.grid$Precision, Recall = hasil.grid$Recall,
Specificity = hasil.grid$Specificity, F1_Score = hasil.grid$F1_Score),
data.frame(Metode = "DRF", ntree = hasil.drf$ntree, mtry = hasil.drf$mtry,
FP = hasil.drf$FP, FN = hasil.drf$FN, Accuracy = hasil.drf$Accuracy,
Precision = hasil.drf$Precision, Recall = hasil.drf$Recall,
Specificity = hasil.drf$Specificity, F1_Score = hasil.drf$F1_Score))
semua <- semua[order(-semua$Accuracy, -semua$F1_Score), ]
head(semua, 6)## Metode ntree mtry FP FN Accuracy Precision Recall Specificity F1_Score
## 13 DRF 500 2 4 0 0.9804 0.9467 1.0000 0.9699 0.9726
## 16 DRF 1000 2 4 0 0.9804 0.9467 1.0000 0.9699 0.9726
## 1 RF 200 2 3 1 0.9804 0.9589 0.9859 0.9774 0.9722
## 4 RF 500 2 4 1 0.9755 0.9459 0.9859 0.9699 0.9655
## 10 DRF 200 2 4 1 0.9755 0.9459 0.9859 0.9699 0.9655
## 17 DRF 1000 3 4 1 0.9755 0.9459 0.9859 0.9699 0.9655
Dari 18 model yang dibandingkan, tiga model berbagi accuracy
tertinggi 0,9804, yaitu Random Forest (num.trees =
200, mtry = 2) serta DRF (ntree = 500,
mtry = 2) dan DRF (ntree = 1000,
mtry = 2); ketiganya hanya salah pada 4 dari 204 data
testing. Perbedaannya ada pada jenis kesalahan. Random
Forest (200, 2) memiliki precision 0,9589, recall
0,9859, specificity 0,9774, dan F1-score 0,9722 (3 FP
dan 1 FN). DRF (500, 2) memiliki precision 0,9467,
recall 1,0000, specificity 0,9699, dan
F1-score 0,9726 (4 FP dan 0 FN). Berdasarkan F1-score
tertinggi dan recall sempurna, DRF (ntree = 500,
mtry = 2) dipilih sebagai model terbaik secara keseluruhan
karena tidak ada pasien ganas yang terlewat, sedangkan DRF dengan 1000
pohon memberi hasil sama tetapi lebih lama dihitung.
Namun, pilihan ini perlu dibaca hati-hati. Selisih F1-score antara kedua model hanya 0,0004, atau setara satu pengamatan, sehingga keduanya dapat dianggap setara. Random Forest (200, 2) tetap menarik karena lebih sederhana, jauh lebih cepat, dan memiliki OOB error sebagai evaluasi internal. Selain itu, model dipilih berdasarkan data testing yang sama dengan yang dipakai menilai sehingga hasilnya cenderung sedikit optimis; pemilihan parameter yang lebih ketat sebaiknya memakai cross-validation.
Random Forest dan Double Random Forest merupakan
metode ensemble berbasis decision tree yang
menggabungkan banyak pohon untuk menurunkan variance dan
meningkatkan kemampuan generalisasi. Random Forest adalah
pengembangan dari bagging yang membentuk setiap pohon dari
sampel bootstrap dan mengacak mtry prediktor pada
setiap node untuk mengurangi korelasi antar-pohon, dengan
prediksi akhir berupa majority vote pada klasifikasi dan
rata-rata pada regresi. Double Random Forest mengembangkan
gagasan tersebut dengan membangun setiap pohon dari seluruh data latih
dan memakai bootstrap hanya pada setiap node untuk
mencari split, sehingga pohonnya cenderung lebih besar dan
beragam tetapi waktu komputasinya lebih lama.
Implementasi Random Forest di R dilakukan dengan fungsi
ranger() dari package ranger dengan
mengatur parameter seperti num.trees, mtry,
min.node.size, replace,
sample.fraction, dan importance. Double
Random Forest belum tersedia sebagai package sehingga
implementasinya dilakukan dengan mengunduh dan mengompilasi kode sumber
dari Han et al. (2020) lalu memakai fungsi DRF.default(),
yang membutuhkan Rtools atau compiler yang sesuai. Tahapan yang
dijalankan meliputi persiapan data, pembagian data training dan
testing dengan set.seed(234), pembentukan model,
prediksi, evaluasi, dan visualisasi untuk klasifikasi pada
dataset BreastCancer, sedangkan regresi dibahas secara
konsep karena sintaks yang tersedia hanya untuk klasifikasi.
Performa model dievaluasi dengan confusion matrix untuk
menghitung accuracy, precision, recall,
specificity, dan F1-score pada data testing,
serta OOB error sebagai evaluasi internal yang diperoleh dari
observasi yang tidak ikut membentuk pohon tertentu (sekitar 36,8% data).
Pada Random Forest dasar, OOB error (0,0292) sangat
dekat dengan error data testing (0,0294), sehingga
dapat dijadikan perkiraan performa tanpa menyisihkan data khusus.
Sebaliknya, DRF tidak memiliki OOB error (bernilai
NaN) karena setiap pohonnya memakai seluruh data
training, sehingga hanya dapat dievaluasi dengan data
testing. Dibandingkan dengan Random Forest, DRF
menghasilkan pohon yang lebih besar (sekitar 61 node dibanding
39 per pohon) dengan waktu komputasi lebih lama, dan accuracy
yang sedikit lebih tinggi pada sebagian besar kombinasi parameter.
Berdasarkan hasil praktikum pada 683 observasi BreastCancer
(479 data training dan 204 data testing), sembilan
model Random Forest menghasilkan accuracy antara
0,9559 sampai 0,9804 dengan OOB error antara 0,0271 sampai
0,0292, dan sembilan model DRF menghasilkan accuracy antara
0,9657 sampai 0,9804. mtry yang lebih kecil cenderung lebih
baik pada kedua metode, sedangkan jumlah pohon hampir tidak memperbaiki
Random Forest tetapi sedikit membantu DRF. DRF unggul pada enam
dari sembilan kombinasi (rata-rata accuracy sekitar 0,9733
dibanding 0,9679). Model terbaik adalah DRF dengan ntree =
500 dan mtry = 2 (accuracy 0,9804,
precision 0,9467, recall 1,0000, specificity
0,9699, F1-score 0,9726), meskipun Random Forest
dengan 200 pohon dan mtry = 2 hampir setara
(F1-score 0,9722). Cell.size, Cell.shape, dan
Bare.nuclei konsisten menjadi tiga variabel terpenting,
sedangkan Mitoses paling kecil pengaruhnya, dengan catatan
bahwa selisih antarmodel hanya berkisar satu sampai beberapa pengamatan
sehingga perlu divalidasi lebih lanjut.
Praktikan diharapkan dapat melanjutkan praktikum dengan menjalankan
Random Forest dan Double Random Forest untuk regresi,
lalu membandingkan keduanya dari sisi akurasi dan waktu komputasi.
Karena selisih performa antarmodel pada praktikum ini hanya satu sampai
beberapa pengamatan, pemilihan mtry dan jumlah pohon
sebaiknya dilakukan dengan cross-validation atau pembagian data
berulang agar tidak terlalu bergantung pada satu pembagian data
testing. Praktikan juga disarankan mencoba ukuran lain seperti
ROC-AUC, mengatur min.node.size atau nodesize,
serta mencoba data lain yang pohon Random Forest-nya lebih
kecil agar keunggulan DRF dapat diuji lebih jelas.
Darwanto, A. R. S., Viarindita, T. L., dan Widyaningsih, Y. (2021). Analisis regresi logistik binomial dan algoritma Random Forest pada proses pengklasifikasian penyakit ginjal kronis. Jurnal Statistika dan Aplikasinya, 5(1), 1–14. doi:10.21009/JSA.05101.
Fransiska, H. (2026). Modul Praktikum Machine Learning dan Model Prediction. Universitas Bengkulu: Bengkulu.
Han, S., Kim, H., dan Lee, Y. S. (2020). Double random forest. Machine Learning, 109, 1569–1586. doi:10.1007/s10994-020-05889-1.
James, G., Witten, D., Hastie, T., dan Tibshirani, R. (2021). An Introduction to Statistical Learning: With Applications in R (edisi ke-2). Springer.
Wright, M. N., dan Ziegler, A. (2017). ranger: A fast implementation of random forests for high dimensional data in C++ and R. Journal of Statistical Software, 77(1), 1–17. doi:10.18637/jss.v077i01.