Human Activity Recognition System (Sistem Pengenal Aktivitas Manusia) atau juga sering disebut HAR System adalah sistem yang bertujuan untuk mengidentifikasi aktivitas manusia. Proses identifikasi tersebut dapat dilakukan dengan cara memanipulasi informasi yang didapatkan dari lingkungan sekitar atau bisa juga dari sensor yang dipasangkan pada tubuh. Pada HAR system yang menggunakan sensor yang dipasangkan pada tubuh menggunakan sensor gerak (accelerometer) yang dipasangkan pada pinggang, pergelangan tangan, dada, dan paha. Berikut ini adalah analisis HAR system dengan menggunakan data hasil pengujian menggunakan accelerometer dan gyroscope yang dihubungkan dengan smartphone. Analisis ini bertujuan untuk membandingkan performa beberapa algoritma yaitu Naive Bayes, Decision Tree, dan Random Forest mana diantara ketiga algoritma tersebut yang dapat memprediksi cukup bagus untuk data set yang diambil dari UCI Machine Learning Repository tentang Human Activity Recognition Using Smartphones.

1. Pra Analisis

Library

library(dplyr) # untuk data wrangling
library(rsample) # untuk melakukan sampling data
library(caret) # untuk pre-process data
library(e1071) # untuk pemodelan Naive Bayes
library(rpart) # untuk pemodelan decision tree
library(rpart.plot)
library(rattle)
library(pROC) #untuk perhitungan auc multiclass
library(tidymodels)

Menyiapkan data yang siap diolah

    # Membaca data dalam bentuk txt
    training_set <- read.table("train/X_train.txt")
    test_set <- read.table("test/X_test.txt")
    data <- rbind(training_set,test_set)
    
    # Menghapus data yang sudah tidak dipakai
    rm(test_set,training_set)
    
    
    # Mengekstrak perhitungan mean dan standart deviasi 
    features_name <- read.table("features.txt")[,2]
    colnames(data) <- features_name
    selected_measures <- grepl('-(mean|std)\\(',features_name)
    data <- subset(data,select=selected_measures)
    
    # Mengubah nama kolom agar lebih mudah dibaca
    colnames(data) <- gsub("mean", "Mean", colnames(data))
    colnames(data) <- gsub("std", "Std", colnames(data))
    colnames(data) <- gsub("^t", "Time", colnames(data))
    colnames(data) <- gsub("^f", "Frequency", colnames(data))
    colnames(data) <- gsub("\\(\\)", "", colnames(data))
    colnames(data) <- gsub("-", "", colnames(data))
    colnames(data) <- gsub("BodyBody", "Body", colnames(data))
    colnames(data) <- gsub("^", "MeanOf", colnames(data))
    
    
    # Membuat label pada data
    activities_train <- read.table("train/y_train.txt")
    activities_test <- read.table("test/y_test.txt")
    activities <- rbind(activities_train,activities_test)[,1]
    labels <- c("WALKING", "WALKING_UPSTAIRS", "WALKING_DOWNSTAIRS",
                "SITTING", "STANDING", "LAYING")
    activities <- labels[activities]
    data <- cbind(Activity = activities,data)
    
    
    subjects_train <- read.table("train/subject_train.txt")
    subjects_test <- read.table("test/subject_test.txt")
    subjects <- rbind(subjects_train,subjects_test)[,1]
    data <- cbind(Subject = subjects,data)
    
    #Menyimpan data dalam bentuk csv
    save_data <- write.table(data,row.name = TRUE,col.names = TRUE, file = "data_clean.csv")    

2. Analisis Data

Read Data

data_clean <- read.csv("data_clean.csv", sep = " ")
head(data_clean)

Fitur yang dipilih untuk database ini berasal dari sinyal mentah akselerometer dan giroskop 3-aksial tAcc-XYZ dan tGyro-XYZ. Sinyal domain waktu ini (awalan ‘t’ untuk menunjukkan waktu) ditangkap pada kecepatan konstan 50 Hz. Kemudian mereka disaring menggunakan median filter dan filter Butterworth low pass orde 3 dengan frekuensi sudut 20 Hz untuk menghilangkan noise. Demikian pula, sinyal akselerasi kemudian dipisahkan menjadi sinyal akselerasi bodi dan gravitasi (tBodyAcc-XYZ dan tGravityAcc-XYZ) menggunakan filter Butterworth lolos rendah lainnya dengan frekuensi sudut 0,3 Hz.

Selanjutnya, percepatan linier tubuh dan kecepatan sudut diturunkan pada waktunya untuk mendapatkan sinyal Jerk (tBodyAccJerk-XYZ dan tBodyGyroJerk-XYZ). Besaran sinyal tiga dimensi ini juga dihitung menggunakan norma Euclidean (tBodyAccMag, tGravityAccMag, tBodyAccJerkMag, tBodyGyroMag, tBodyGyroJerkMag).

Fast Fourier Transform (FFT) diterapkan ke beberapa sinyal ini yang menghasilkan fBodyAcc-XYZ, fBodyAccJerk-XYZ, fBodyGyro-XYZ, fBodyAccJerkMag, fBodyGyroMag, fBodyGyroJerkMag. (Perhatikan ‘f’ untuk menunjukkan sinyal domain frekuensi).

Sinyal ini digunakan untuk memperkirakan variabel vektor fitur untuk setiap pola: ‘-XYZ’ digunakan untuk menunjukkan sinyal 3-aksial dalam arah X, Y, dan Z.

  • tBodyAcc-XYZ
  • tGravityAcc-XYZ
  • tBodyAccJerk-XYZ
  • tBodyGyro-XYZ
  • tBodyGyroJerk-XYZ
  • tBodyAccMag
  • tGravityAccMag
  • tBodyAccJerkMag
  • tBodyGyroMag
  • tBodyGyroJerkMag
  • fBodyAcc-XYZ
  • fBodyAccJerk-XYZ
  • fBodyGyro-XYZ
  • fBodyAccMag
  • fBodyAccJerkMag
  • fBodyGyroMag
  • fBodyGyroJerkMag

Kumpulan variabel yang diestimasi dari sinyal-sinyal ini adalah:

  • mean (): Nilai rata-rata
  • std (): Simpangan baku
  • mad (): Deviasi absolut median
  • max (): Nilai terbesar dalam larik
  • min (): Nilai terkecil dalam larik
  • sma (): Area besaran sinyal
  • energy (): Pengukuran energi. Jumlah kotak dibagi dengan jumlah nilai.
  • iqr (): Rentang interkuartil
  • entropi (): Entropi sinyal
  • arCoeff (): Koefisien Autorregresion dengan urutan Burg sama dengan 4
  • korelasi (): koefisien korelasi antara dua sinyal
  • maxInds (): indeks komponen frekuensi dengan magnitudo terbesar
  • meanFreq (): Rata-rata tertimbang dari komponen frekuensi untuk mendapatkan frekuensi rata-rata
  • skewness (): kemiringan sinyal domain frekuensi
  • kurtosis (): kurtosis dari sinyal domain frekuensi
  • bandEnergy (): Energi interval frekuensi dalam 64 bins FFT dari setiap jendela.
  • angle (): Sudut antar vektor.

Vektor tambahan diperoleh dengan rata-rata sinyal dalam sampel jendela sinyal. Ini digunakan pada variabel angle ():

  • GravitasiMean
  • tBodyAccMean
  • tBodyAccJerkMean
  • tBodyGyroMean
  • tBodyGyroJerkMean

Data Wrangling

Setelah data dipersiapkan untuk diolah maka struktur data adalah sebagai berikut:

glimpse(data_clean)
## Rows: 10,299
## Columns: 68
## $ Subject                            <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,~
## $ Activity                           <chr> "STANDING", "STANDING", "STANDING",~
## $ MeanOfTimeBodyAccMeanX             <dbl> 0.2885845, 0.2784188, 0.2796531, 0.~
## $ MeanOfTimeBodyAccMeanY             <dbl> -0.020294171, -0.016410568, -0.0194~
## $ MeanOfTimeBodyAccMeanZ             <dbl> -0.13290514, -0.12352019, -0.113461~
## $ MeanOfTimeBodyAccStdX              <dbl> -0.9952786, -0.9982453, -0.9953796,~
## $ MeanOfTimeBodyAccStdY              <dbl> -0.9831106, -0.9753002, -0.9671870,~
## $ MeanOfTimeBodyAccStdZ              <dbl> -0.9135264, -0.9603220, -0.9789440,~
## $ MeanOfTimeGravityAccMeanX          <dbl> 0.9633961, 0.9665611, 0.9668781, 0.~
## $ MeanOfTimeGravityAccMeanY          <dbl> -0.1408397, -0.1415513, -0.1420098,~
## $ MeanOfTimeGravityAccMeanZ          <dbl> 0.115374940, 0.109378810, 0.1018839~
## $ MeanOfTimeGravityAccStdX           <dbl> -0.9852497, -0.9974113, -0.9995740,~
## $ MeanOfTimeGravityAccStdY           <dbl> -0.9817084, -0.9894474, -0.9928658,~
## $ MeanOfTimeGravityAccStdZ           <dbl> -0.8776250, -0.9316387, -0.9929172,~
## $ MeanOfTimeBodyAccJerkMeanX         <dbl> 0.07799634, 0.07400671, 0.07363596,~
## $ MeanOfTimeBodyAccJerkMeanY         <dbl> 0.005000803, 0.005771104, 0.0031040~
## $ MeanOfTimeBodyAccJerkMeanZ         <dbl> -0.0678308080, 0.0293766330, -0.009~
## $ MeanOfTimeBodyAccJerkStdX          <dbl> -0.9935191, -0.9955481, -0.9907428,~
## $ MeanOfTimeBodyAccJerkStdY          <dbl> -0.9883600, -0.9810636, -0.9809556,~
## $ MeanOfTimeBodyAccJerkStdZ          <dbl> -0.9935750, -0.9918457, -0.9896866,~
## $ MeanOfTimeBodyGyroMeanX            <dbl> -0.006100849, -0.016111620, -0.0316~
## $ MeanOfTimeBodyGyroMeanY            <dbl> -0.03136479, -0.08389378, -0.102335~
## $ MeanOfTimeBodyGyroMeanZ            <dbl> 0.10772540, 0.10058429, 0.09612688,~
## $ MeanOfTimeBodyGyroStdX             <dbl> -0.9853103, -0.9831200, -0.9762921,~
## $ MeanOfTimeBodyGyroStdY             <dbl> -0.9766234, -0.9890458, -0.9935518,~
## $ MeanOfTimeBodyGyroStdZ             <dbl> -0.9922053, -0.9891212, -0.9863787,~
## $ MeanOfTimeBodyGyroJerkMeanX        <dbl> -0.09916740, -0.11050283, -0.108485~
## $ MeanOfTimeBodyGyroJerkMeanY        <dbl> -0.05551737, -0.04481873, -0.042410~
## $ MeanOfTimeBodyGyroJerkMeanZ        <dbl> -0.061985797, -0.059242822, -0.0558~
## $ MeanOfTimeBodyGyroJerkStdX         <dbl> -0.9921107, -0.9898726, -0.9884618,~
## $ MeanOfTimeBodyGyroJerkStdY         <dbl> -0.9925193, -0.9972926, -0.9956321,~
## $ MeanOfTimeBodyGyroJerkStdZ         <dbl> -0.9920553, -0.9938510, -0.9915318,~
## $ MeanOfTimeBodyAccMagMean           <dbl> -0.9594339, -0.9792892, -0.9837031,~
## $ MeanOfTimeBodyAccMagStd            <dbl> -0.9505515, -0.9760571, -0.9880196,~
## $ MeanOfTimeGravityAccMagMean        <dbl> -0.9594339, -0.9792892, -0.9837031,~
## $ MeanOfTimeGravityAccMagStd         <dbl> -0.9505515, -0.9760571, -0.9880196,~
## $ MeanOfTimeBodyAccJerkMagMean       <dbl> -0.9933059, -0.9912535, -0.9885313,~
## $ MeanOfTimeBodyAccJerkMagStd        <dbl> -0.9943364, -0.9916944, -0.9903969,~
## $ MeanOfTimeBodyGyroMagMean          <dbl> -0.9689591, -0.9806831, -0.9763171,~
## $ MeanOfTimeBodyGyroMagStd           <dbl> -0.9643352, -0.9837542, -0.9860515,~
## $ MeanOfTimeBodyGyroJerkMagMean      <dbl> -0.9942478, -0.9951232, -0.9934032,~
## $ MeanOfTimeBodyGyroJerkMagStd       <dbl> -0.9913676, -0.9961016, -0.9950910,~
## $ MeanOfFrequencyBodyAccMeanX        <dbl> -0.9947832, -0.9974507, -0.9935941,~
## $ MeanOfFrequencyBodyAccMeanY        <dbl> -0.9829841, -0.9768517, -0.9725115,~
## $ MeanOfFrequencyBodyAccMeanZ        <dbl> -0.9392687, -0.9735227, -0.9833040,~
## $ MeanOfFrequencyBodyAccStdX         <dbl> -0.9954217, -0.9986803, -0.9963128,~
## $ MeanOfFrequencyBodyAccStdY         <dbl> -0.9831330, -0.9749298, -0.9655059,~
## $ MeanOfFrequencyBodyAccStdZ         <dbl> -0.9061650, -0.9554381, -0.9770493,~
## $ MeanOfFrequencyBodyAccJerkMeanX    <dbl> -0.9923325, -0.9950322, -0.9909937,~
## $ MeanOfFrequencyBodyAccJerkMeanY    <dbl> -0.9871699, -0.9813115, -0.9816423,~
## $ MeanOfFrequencyBodyAccJerkMeanZ    <dbl> -0.9896961, -0.9897398, -0.9875663,~
## $ MeanOfFrequencyBodyAccJerkStdX     <dbl> -0.9958207, -0.9966523, -0.9912488,~
## $ MeanOfFrequencyBodyAccJerkStdY     <dbl> -0.9909363, -0.9820839, -0.9814148,~
## $ MeanOfFrequencyBodyAccJerkStdZ     <dbl> -0.9970517, -0.9926268, -0.9904159,~
## $ MeanOfFrequencyBodyGyroMeanX       <dbl> -0.9865744, -0.9773867, -0.9754332,~
## $ MeanOfFrequencyBodyGyroMeanY       <dbl> -0.9817615, -0.9925300, -0.9937147,~
## $ MeanOfFrequencyBodyGyroMeanZ       <dbl> -0.9895148, -0.9896058, -0.9867557,~
## $ MeanOfFrequencyBodyGyroStdX        <dbl> -0.9850326, -0.9849043, -0.9766422,~
## $ MeanOfFrequencyBodyGyroStdY        <dbl> -0.9738861, -0.9871681, -0.9933990,~
## $ MeanOfFrequencyBodyGyroStdZ        <dbl> -0.9940349, -0.9897847, -0.9873282,~
## $ MeanOfFrequencyBodyAccMagMean      <dbl> -0.9521547, -0.9808566, -0.9877948,~
## $ MeanOfFrequencyBodyAccMagStd       <dbl> -0.9561340, -0.9758658, -0.9890155,~
## $ MeanOfFrequencyBodyAccJerkMagMean  <dbl> -0.9937257, -0.9903355, -0.9892801,~
## $ MeanOfFrequencyBodyAccJerkMagStd   <dbl> -0.9937550, -0.9919603, -0.9908667,~
## $ MeanOfFrequencyBodyGyroMagMean     <dbl> -0.9801349, -0.9882956, -0.9892548,~
## $ MeanOfFrequencyBodyGyroMagStd      <dbl> -0.9613094, -0.9833219, -0.9860277,~
## $ MeanOfFrequencyBodyGyroJerkMagMean <dbl> -0.9919904, -0.9958539, -0.9950305,~
## $ MeanOfFrequencyBodyGyroJerkMagStd  <dbl> -0.9906975, -0.9963995, -0.9951274,~

Cek missing value pada data

anyNA(data_clean)
## [1] FALSE

Setelah kita tahu bahwa data yang dipakai tidak mengandung missing value, selanjutnya mengubah tipe data kolom Activity menjadi factor dan membuang kolom subject karena tidak akan digunakan dalam proses analisis.

data_clean <- data_clean %>% 
  select(-Subject) %>% 
  mutate(Activity = as.factor(Activity))

head(data_clean)

Cross Validation

Splitting dataset menjadi data train dan data test dengan perbandingan 80% data train dan 20% data test.

set.seed(100)
splitted <- initial_split(data = data_clean, prop = 0.8, strata = "Activity")
train <- training(splitted)
test <- testing(splitted)

Melakukan pengecekan proporsi kelas target apakah sudah seimbang atau belum.

prop.table(table(train$Activity))
## 
##             LAYING            SITTING           STANDING            WALKING 
##          0.1887891          0.1725309          0.1850279          0.1671924 
## WALKING_DOWNSTAIRS   WALKING_UPSTAIRS 
##          0.1364960          0.1499636

Modeling Naive Bayes

Naïve Bayes Classifier merupakan sebuah metoda klasifikasi yang berakar pada teorema Bayes . Metode pengklasifikasian dg menggunakan metode probabilitas dan statistik yg dikemukakan oleh ilmuwan Inggris Thomas Bayes , yaitu memprediksi peluang di masa depan berdasarkan pengalaman di masa sebelumnya sehingga dikenal sebagai Teorema Bayes . Ciri utama dr Naïve Bayes Classifier ini adalah asumsi yg sangat kuat (naïf) akan independensi dari masing-masing kondisi / kejadian.

Karakteristik Naive Bayes:

  • Metode Naive Bayes bekerja teguh (robust) terhadap data-data yang terisolasi yang biasanya merupakan data dengan karakteristik berbeda (outliner). Naive Bayes juga bisa menangani nilai atribut yang salah dengan mengabaikan data latih selama proses pembangunan model dan prediksi.
  • Tangguh menghadapi atribut yang tidak relevan.
  • Atribut yang mempunyai korelasi bisa mendegradasi kinerja klasifikasi Naïve Bayes karena asumsi independensi atribut tersebut sudah tidak ada.
  • Lebih sesuai untuk data dengan prediktor kategorik. Ini karena Naive Bayes sensitif terhadap kelangkaan data. Sedangkan variabel kontinu mungkin mengandung sangat langka atau bahkan hanya satu pengamatan untuk nilai tertentu.

Kelebihan Naive Bayes:

Pembuatannya simple dan mudah dipahami tetapi powerful

Kekurangan Naive Bayes:

Apabila probabilitas kondisionalnya bernilai nol, maka probabilitas prediksi juga akan bernilai nol (Skewness due to the data scarcity). Hal ini dapat diatasi dengan cara menghapus variabel prediktor yang kemunculannya 0 di salah satu kelas atau dengan cara melakukan laplace smoothing yaitu Menambahkan angka kecil(misal 1) pada kemunculan tiap prediktor agar peluang di suatu kelas tidak 0.

# Pembuatan model naive bayes
naive <- naiveBayes(Activity~., data = train, laplace = 1)
# Melakukan prediksi
pred_test <- predict(naive, test, type = "class")
pred_test_prob <- predict(naive, test, type = "raw")
naive_table <- select(test, Activity) %>%
  bind_cols(activity_pred = pred_test) %>% 
  bind_cols(laying_pred= round(pred_test_prob[,1],4)) %>% 
  bind_cols(sitting_pred = round(pred_test_prob[,2],4)) %>% 
  bind_cols(standing_pred = round(pred_test_prob[,3],4)) %>% 
  bind_cols(walking_pred = round(pred_test_prob[,4],4)) %>% 
  bind_cols(walking_downstair_pred = round(pred_test_prob[,5],4)) %>% 
  bind_cols(walking_upstair_pred = round(pred_test_prob[,6],4))
# Evaluasi performa model
naive_table %>% 
  conf_mat(Activity, activity_pred) %>% 
  autoplot(type = "heatmap")

# Menyimpan metrik-metrik untuk dibandingkan dengan model lain
naive_metrics <- naive_table %>%
  summarise(
    accuracy = accuracy_vec(Activity, activity_pred),
    sensitivity = sens_vec(Activity, activity_pred),
    specificity = spec_vec(Activity, activity_pred),
    precision = precision_vec(Activity, activity_pred)
  )
naive_metrics

Berikut adalah metrik yang akan digunakan sebagai evaluasi dari model:

  • Akurasi (Accuracy): kemampuan memprediksi dengan benar kedua kelas dari observasi total.
  • Presisi(Precision): kemampuan memprediksi dengan benar kelas positif dari total kelas prediksi-positif (false positif rendah).
  • Recall (Sensitivity): kemampuan untuk memprediksi kelas positif dengan benar dari total kelas positif aktual (false negatif rendah).
  • Specificity: kemampuan memprediksi dengan benar kelas negatif dari total kelas negatif aktual.

Pada kasus ini, kami ingin mengkonstruksi model yang dapat se-presisi mungkin untuk memprediksi aktivitas manusia. Jadi, metrik yang akan digunakan adalah metrik presisi. Dimana darai hasil prediksi diperoleh nilai presisi menggunakan algoritma Naive Bayes adalah sebesar 83,4% sehinnga model Naive Bayes dikatakan cukup bagus.

Modeling Decision Tree

Decision tree adalah sebuah diagram alir yang berbentuk seperti struktur pohon yang mana setiap internal node menyatakan pengujian terhadap suatu atribut, setiap cabang menyatakan output dari pegujian tersebut dan leaf node menyatakan kelas–kelas atau distribusi kelas.

Struktur Decision Tree:

  • Root Node: Node pertama/akar, berupa variabel yang paling utama digunakan untuk menentukan nilai target
  • Internal Node: Node kedua/ranting dan seterusnya, berupa variabel lain yang digunakan apabila cabang pertama tidak cukup untuk menentukan target
  • Leaf/Terminal Node: Nilai target atau kelas yang diprediksi

Berikut adalah ilustrasi tree yang akan dihasilkan oleh model decision tree.

Untuk membuat model decision tree kita dapat menggunakan library(partykit) dengan menggunakan fungsi ctree(), namun kali ini akan digunakan library(rpart) sebagai cara lain untuk membuat model decision tree dan menggunakan library(rpart.plot) dan library(rattle) untuk melakukan visualisasi tree.

library(rpart)
library(rpart.plot)
library(rattle)

# Membuat model tree menggunakan fungsi rpart()
dec_tree <- rpart(formula = Activity~., data = train, method = "class")

# Visualisasi tree
fancyRpartPlot(dec_tree, sub = NULL)

# Melakukan prediksi
tree_pred <- predict(dec_tree, test, type = "class")
tree_pred_prob <- predict(dec_tree, test, type = "prob")
# Evaluasi performa model menggunakan confusion matriks dari library caret
confusionMatrix(tree_pred, reference = test$Activity)
## Confusion Matrix and Statistics
## 
##                     Reference
## Prediction           LAYING SITTING STANDING WALKING WALKING_DOWNSTAIRS
##   LAYING                387       0        0       0                  0
##   SITTING                 0     308       55       0                  0
##   STANDING                0      47      326       0                  0
##   WALKING                 0       0        0     293                 37
##   WALKING_DOWNSTAIRS      1       0        0       3                220
##   WALKING_UPSTAIRS        0       0        0      48                 24
##                     Reference
## Prediction           WALKING_UPSTAIRS
##   LAYING                            0
##   SITTING                           0
##   STANDING                          0
##   WALKING                          59
##   WALKING_DOWNSTAIRS               15
##   WALKING_UPSTAIRS                234
## 
## Overall Statistics
##                                           
##                Accuracy : 0.8595          
##                  95% CI : (0.8437, 0.8742)
##     No Information Rate : 0.1886          
##     P-Value [Acc > NIR] : < 2.2e-16       
##                                           
##                   Kappa : 0.8309          
##                                           
##  Mcnemar's Test P-Value : NA              
## 
## Statistics by Class:
## 
##                      Class: LAYING Class: SITTING Class: STANDING
## Sensitivity                 0.9974         0.8676          0.8556
## Specificity                 1.0000         0.9677          0.9720
## Pos Pred Value              1.0000         0.8485          0.8740
## Neg Pred Value              0.9994         0.9723          0.9673
## Prevalence                  0.1886         0.1726          0.1852
## Detection Rate              0.1881         0.1497          0.1585
## Detection Prevalence        0.1881         0.1765          0.1813
## Balanced Accuracy           0.9987         0.9176          0.9138
##                      Class: WALKING Class: WALKING_DOWNSTAIRS
## Sensitivity                  0.8517                    0.7829
## Specificity                  0.9440                    0.9893
## Pos Pred Value               0.7532                    0.9205
## Neg Pred Value               0.9694                    0.9664
## Prevalence                   0.1672                    0.1366
## Detection Rate               0.1424                    0.1070
## Detection Prevalence         0.1891                    0.1162
## Balanced Accuracy            0.8979                    0.8861
##                      Class: WALKING_UPSTAIRS
## Sensitivity                           0.7597
## Specificity                           0.9588
## Pos Pred Value                        0.7647
## Neg Pred Value                        0.9577
## Prevalence                            0.1497
## Detection Rate                        0.1138
## Detection Prevalence                  0.1488
## Balanced Accuracy                     0.8593
tree_table <- select(test, Activity) %>%
  bind_cols(activity_pred = tree_pred) %>% 
  bind_cols(laying_pred= round(tree_pred_prob[,1],4)) %>% 
  bind_cols(sitting_pred = round(tree_pred_prob[,2],4)) %>% 
  bind_cols(standing_pred = round(tree_pred_prob[,3],4)) %>% 
  bind_cols(walking_pred = round(tree_pred_prob[,4],4)) %>% 
  bind_cols(walking_downstair_pred = round(tree_pred_prob[,5],4)) %>% 
  bind_cols(walking_upstair_pred = round(tree_pred_prob[,6],4))
tree_table %>% 
  conf_mat(Activity, activity_pred) %>% 
  autoplot(type = "heatmap")

# Menyimpan metrik-metrik untuk dibandingkan dengan model lain
tree_metrics <- tree_table %>%
  summarise(
    accuracy = accuracy_vec(Activity, activity_pred),
    sensitivity = sens_vec(Activity, activity_pred),
    specificity = spec_vec(Activity, activity_pred),
    precision = precision_vec(Activity, activity_pred)
  )
tree_metrics

Dari hasil analisis model, diperoleh metriks presisi sebesar 86%, dimana dengan menggunakan model decision tree diperoleh hasil yang cukup baik.

Modeling Random Forest

Random Forest merupakan salah satu metode dalam Decision Tree. Random Forest melakukan prediksi dengan membuat sekumpulan banyak Decision Tree. Masing-masing Decision Tree memiliki karakteristik masing-masing dan tidak saling berhubungan satu sama lain. Random Forest kemudian melakukan prediksi untuk masing-masing decision tree, kemudian dari sekian banyak hasil prediksi tersebut dilakukan voting. Kelas dengan jumlah terbanyak akan menjadi hasil prediksi final.

Berikut adalah proses yang terjadi:

  • Membuat data dengan random sampling dari data yang ada dan mengizinkan adanya duplikat (Bootstrap sampling).
  • Membuat decision tree dari masing-masing data hasil bootstrap sampling, digunakan prediktor sebanyak mtry yang juga dipilih secara random.
  • Melakukan prediksi data baru untuk setiap decision tree.
  • Melakukan majority voting untuk menentukan hasil prediksi final (Aggregation).

Sebelum membuat model random forest, berikut adalah salah satu pengetahuan tambahan terkait K-Fold Cross Validation. K-Fold Cross Validation adalah salah satu dari jenis pengujian cross validation yang berfungsi untuk menilai kinerja proses sebuah metode algoritme dengan membagi sampel data secara acak dan mengelompokkan data tersebut sebanyak nilai K k-fold. Berikut adalah ilustrasi dari K-Fold Cross Validation.

Berikut adalah pengaplikasian model random forest menggunakan data Human Activity Recognition yang sudah kita simpan ke dalam data_clean. Pada kali ini kita akan membuat model random forest dan menyimpannya kedalam bentuk file RDS. File RDS adalah format file eksternal dari objek yang ada di R, dimana dengan adanya file RDS ini dapat mempercepat proses komputasi untuk model random forest. Jika kita tidak menyimpan kedalam file RDS maka komputasi yang diperlukan untuk membangun model random forest cukup lama.

# Don't run this code
set.seed(123)
ctrl <- trainControl(method = "repeatedcv", number = 5, repeats = 4)
model_forest <- train(Activity ~., data = train, method = "rf", trControl= ctrl)
model_forest
# Menyimpan model random forest ke file RDS
saveRDS(model_forest, file = "model_forest.rds")
# Membaca file RDS
model_forest <- readRDS("model_forest.rds")
model_forest
## Random Forest 
## 
## 8242 samples
##   66 predictor
##    6 classes: 'LAYING', 'SITTING', 'STANDING', 'WALKING', 'WALKING_DOWNSTAIRS', 'WALKING_UPSTAIRS' 
## 
## No pre-processing
## Resampling: Cross-Validated (5 fold, repeated 4 times) 
## Summary of sample sizes: 6593, 6593, 6592, 6595, 6595, 6593, ... 
## Resampling results across tuning parameters:
## 
##   mtry  Accuracy   Kappa    
##    2    0.9640566  0.9567573
##   34    0.9704257  0.9644212
##   66    0.9662097  0.9593486
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was mtry = 34.

Setelah itu kita dapat melakukan inspeksi model dan mengetahui variabel apa saja yang sebenarnya dianggap penting ata paling informatif untuk memprediksi kelas target. Kita dapat menggunakan fungsi varImp().

varImp(model_forest)
## rf variable importance
## 
##   only 20 most important variables shown (out of 66)
## 
##                                  Overall
## MeanOfTimeGravityAccMeanX        100.000
## MeanOfTimeGravityAccMeanY         67.929
## MeanOfTimeBodyAccJerkStdX         24.234
## MeanOfTimeBodyAccMagStd           22.839
## MeanOfFrequencyBodyAccMeanX       21.491
## MeanOfTimeGravityAccMagStd        21.090
## MeanOfFrequencyBodyAccJerkStdX    20.196
## MeanOfTimeGravityAccMeanZ         18.134
## MeanOfTimeBodyAccJerkMagMean      17.304
## MeanOfFrequencyBodyAccMagStd      16.625
## MeanOfFrequencyBodyAccJerkStdY    13.324
## MeanOfFrequencyBodyAccJerkMeanX   10.153
## MeanOfFrequencyBodyGyroStdX        8.041
## MeanOfTimeGravityAccStdX           7.835
## MeanOfFrequencyBodyAccJerkMagStd   6.894
## MeanOfTimeBodyGyroMeanX            5.152
## MeanOfTimeGravityAccStdY           4.765
## MeanOfTimeBodyGyroStdX             4.748
## MeanOfTimeBodyAccJerkStdY          4.469
## MeanOfFrequencyBodyAccMagMean      4.258

Selanjutnya kita dapat melihat nilai OOB dengan melakukan visualisasi pada model random forest.

plot(model_forest$finalModel)
legend("topright", colnames(model_forest$finalModel$err.rate),col=1:6,cex=0.8,fill=1:6)

model_forest$finalModel
## 
## Call:
##  randomForest(x = x, y = y, mtry = param$mtry) 
##                Type of random forest: classification
##                      Number of trees: 500
## No. of variables tried at each split: 34
## 
##         OOB estimate of  error rate: 2.6%
## Confusion matrix:
##                    LAYING SITTING STANDING WALKING WALKING_DOWNSTAIRS
## LAYING               1555       1        0       0                  0
## SITTING                 0    1358       63       0                  0
## STANDING                0      55     1470       0                  0
## WALKING                 0       0        0    1357                 13
## WALKING_DOWNSTAIRS      0       0        0      15               1084
## WALKING_UPSTAIRS        0       0        0      16                 16
##                    WALKING_UPSTAIRS  class.error
## LAYING                            0 0.0006426735
## SITTING                           1 0.0450070323
## STANDING                          0 0.0360655738
## WALKING                           8 0.0152394775
## WALKING_DOWNSTAIRS               26 0.0364444444
## WALKING_UPSTAIRS               1204 0.0258899676

Selanjutnya melakukan prediksi dengan menggunakan model rando forest.

# Melakukan prediksi
rf_pred <- predict(model_forest, test, type = "raw")
rf_prob <- predict(model_forest, test, type = "prob")

# hasil
rf_table <- select(test, Activity) %>%
  bind_cols(activity_pred = rf_pred) %>% 
  bind_cols(laying_pred= round(rf_prob[,1],4)) %>% 
  bind_cols(sitting_pred = round(rf_prob[,2],4)) %>% 
  bind_cols(standing_pred = round(rf_prob[,3],4)) %>% 
  bind_cols(walking_pred = round(rf_prob[,4],4)) %>% 
  bind_cols(walking_downstair_pred = round(rf_prob[,5],4)) %>% 
  bind_cols(walking_upstair_pred = round(rf_prob[,6],4))

# performance evaluation - confusion matrix 
rf_table %>% 
  conf_mat(Activity, activity_pred) %>% 
  autoplot(type = "heatmap")

# Menyimpan metrik-metrik untuk dibandingkan dengan model lain
rf_metrics <- rf_table %>%
  summarise(
    accuracy = accuracy_vec(Activity, activity_pred),
    sensitivity = sens_vec(Activity, activity_pred),
    specificity = spec_vec(Activity, activity_pred),
    precision = precision_vec(Activity, activity_pred))
rf_metrics

3. Kesimpulan

rbind("Naive Bayes" = naive_metrics, "Decision Tree" = tree_metrics, "Random Forest" = rf_metrics)

Dari tabel diatas dapat diketahui model random forest memiliki tingkat presisi yang tertinggi yaitu 97,1% dibandingkan dengan model Naive Bayes dan Decision Tree. Model Random juga memberikan hasil diatas 90% untuk Akurasi, Sensitivity dan Specificity. Hasil analisis ini masih dapat ditingkatkan performanya dengan mengubah dan mencari nilai cutoff yang paling sesuai, atau dengan melakukan validasi ulang terhadap nilai k-fold khususnya pada model random forest.