20  지도학습

#
if(!require("tidyverse")) install.packages("tidyverse")
if(!require("dslabs")) install.packages("dslabs")
if(!require("caret")) install.packages("caret")
if(!require("purrr")) install.packages("purrr")
if(!require("randomForest")) install.packages("randomForest")
if(!require("doParallel")) install.packages("doParallel")
if(!require("foreach")) install.packages("foreach")
if(!require("tictoc")) install.packages("tictoc")

20.1 실습을 위한 시뮬레이션 데이터

R에서 기계 학습 실습을 위한 시뮬레이션 데이터를 생성하는 것은 데이터 과학과 분석의 기본 개념을 이해하는 데 매우 효과적인 방법입니다. 아래의 예제에서는 성별, 교육 수준, 나이, 소득, 근무 시간, 그리고 PHQ-9 설문조사 응답과 같은 변수를 포함한 데이터를 생성합니다. 이러한 실습은 데이터를 조작하고 준비하는 과정을 이해하는 데 특히 유용하며, 이는 기계 학습 워크플로우의 중요한 단계입니다.

set.seed(2023) # For reproducible results
n <- 10000 # Sample size

set.seed(2023):

  • 역할: R의 난수 생성기를 고정하는 함수로, 결과의 재현성을 보장합니다.
  • 중요성: 동일한 seed 값을 사용하면 항상 동일한 난수 시퀀스가 생성되므로, 과학 연구나 코드 공유 시 결과를 쉽게 복제할 수 있습니다.
  • 기계 학습에서 활용: 모델 성능 비교, 디버깅, 협업 프로젝트에서 중요한 역할을 합니다.

n <- 10000:

  • 설명: 샘플 크기를 10,000으로 설정합니다. 이는 데이터 세트에서 생성할 관측치(행)의 수를 의미합니다.

  • 샘플 크기의 영향:

    • 샘플 크기가 클수록 분석 및 모델링 결과가 더 신뢰할 수 있고 안정적입니다.
    • 하지만 더 큰 데이터 세트는 더 많은 계산 자원을 요구합니다.
    mm <- tibble(
      Gender    = sample(c("Male", "Female"), n, replace = TRUE), 
      Education = sample(c("2.Middle",  "1.High", "0.Univ"), n, replace = TRUE), 
      Age       = sample(c(30:70), n, replace=TRUE), 
      Income    = sample(c(1:10)*100, n, replace = TRUE ), 
      working_h = sample(c(35:65), n, replace = TRUE),
    ) %>% 
      mutate(agegp = case_when(
        Age <30 ~ "<30", 
        Age <40 ~ "<40", 
        Age <50 ~ "<50", 
        Age <60 ~ "<60", 
        TRUE ~ "\u226560" 
      )) %>%
      mutate(incgp = case_when(
        Income < 300 ~ "<300", 
        Income < 500 ~ "<500", 
        Income < 700 ~ "<700", 
        TRUE ~ "\u2265700"
      )) %>%
      mutate(whgp = case_when(
        working_h < 45 ~ "<45", 
        working_h < 55 ~ "<55", 
        TRUE ~ "\u226555"
      )) %>%
      cbind(.,
            replicate(9, integer(n)) %>%
              data.frame(.) %>% setNames(paste0("Q", 1:9))
    )

다음 코드는 PHQ-9 설문 응답(Q 항목)을 성별, 나이, 교육 수준, 근무 시간과 같은 인구통계적 및 개인적 특성에 기반하여 일련의 조건부 변환을 수행합니다.

  • 성별 기반 변환: PHQ-9 응답(Q 항목)을 성별에 따라 확률 분포를 사용해 수정합니다.
  • 나이 기반 조정: 나이 그룹별로 서로 다른 확률을 사용해 Q 응답 값을 조정합니다.
  • 교육 수준 기반 변환: 교육 수준에 따라 Q 응답 값을 서로 다른 확률 분포를 적용하여 변경합니다.
  • 근무 시간 조정: 근무 시간에 따라 Q 응답 값을 서로 다른 확률 분포로 조정합니다.
  • 응답 최종 조정: 0으로 유지되는 응답은 그대로 두며, 다른 값은 확률에 따라 감소시킵니다.
mm= mm %>% tibble() %>%
  mutate(across(
    .cols = starts_with("Q"), 
    .fns  = ~ case_when(
      Gender == "Male" ~ sample(0:3, length(.), replace = TRUE, prob = c(0.8,  0.1, 0.05, 0.05)), 
      TRUE             ~ sample(0:3, length(.), replace = TRUE, prob = c(0.7,  0.15, 0.1, 0.05))
    )
  )) %>%
  mutate(across(
    .cols = starts_with("Q"), 
    .fns  = ~ case_when(
      Age <30 ~ . + sample(0:1, length(.), replace = TRUE, prob = c(0.90,  0.1)),
      Age <40 ~ . + sample(0:1, length(.), replace = TRUE, prob = c(0.85,  0.15)), 
      Age <50 ~ . + sample(0:1, length(.), replace = TRUE, prob = c(0.80,  0.2)), 
      Age <60 ~ . + sample(0:1, length(.), replace = TRUE, prob = c(0.60,  0.4)), 
      TRUE    ~ . + sample(0:1, length(.), replace = TRUE, prob = c(0.40,  0.6))
  ))) %>%
  mutate(across(
    .cols = starts_with("Q"), 
    .fns  = ~ case_when(
      Education == "0.Univ"   ~ . + sample(0:1, length(.), replace = TRUE, prob = c(0.95,  0.05)),
      Education == "1.High"   ~ . + sample(0:1, length(.), replace = TRUE, prob = c(0.90,  0.1)),
      Education == "2.Middle" ~ . + sample(0:1, length(.), replace = TRUE, prob = c(0.85,  0.15)),
    )
  )) %>%
  mutate(across(
    .cols = starts_with("Q"), 
    .fns  = ~ case_when(
      working_h <45 ~ . + sample(0:1, length(.), replace = TRUE, prob = c(0.95, 0.05)), 
      working_h <55 ~ . + sample(0:1, length(.), replace = TRUE, prob = c(0.90, 0.1)), 
      TRUE          ~ . + sample(0:1, length(.), replace = TRUE, prob = c(0.70, 0.3))
    )
  )) %>%
  mutate(across(
    .cols = starts_with("Q"), 
    .fns  = ~ case_when(
      . == 0 ~ 0, 
      TRUE   ~ . - sample(0:1, length(.), replace = TRUE, prob = c(0.8, 0.2))
    )
  )) %>%
  mutate(phqsum = rowSums(across(starts_with("Q")))) %>%
  mutate(phq_level =case_when(
    phqsum <= 4  ~ "0.None",
    phqsum <= 9  ~ "1.Mild", 
    phqsum <= 14 ~ "2.Moderate", 
    phqsum <= 19 ~ "3.Moderate Severe", 
    TRUE         ~ "4.Severe", 
  )) %>%
  mutate(depressive = ifelse(phqsum >=10, "Depressive", "None"))

20.2 연속형 결과 예측

20.2.1 훈련 세트 vs 테스트 세트

훈련 세트 (Training Set)

  • 목적: 훈련 세트는 기계 학습 모델을 학습시키거나 적합(fit)하는 데 사용됩니다. 즉, 모델이 데이터 하위 집합에서 패턴, 관계, 또는 특징을 학습하는 역할을 합니다.

  • 크기:일반적으로 데이터의 더 큰 부분(70-80%)이 훈련에 할당됩니다. 이는 모델 성능이 주로 학습 데이터의 양과 품질에 따라 좌우되기 때문입니다.

  • 사용 방식:훈련 중 모델은 입력 특징(feature)에 따라 예측하거나 분류하며, 사전에 정의된 손실 함수(loss function)를 기준으로 오류를 줄이기 위해 내부 파라미터(예: 신경망의 경우 가중치)를 조정합니다.

테스트 세트 (Testing Set)

  • 목적: 테스트 세트는 학습된 모델의 성능과 일반화 능력을 평가하는 데 사용됩니다. 이는 모델에게 새로운 데이터를 제공하여 그 성능을 검증합니다.

  • 크기: 데이터의 더 작은 부분(20-30%)이 테스트용으로 예약됩니다. 테스트 데이터는 모델 성능을 신뢰성 있게 평가할 만큼 충분히 확보하면서도, 훈련 세트 크기를 너무 줄이지 않아야 합니다.

  • 사용방식: 모델을 훈련한 후, 테스트 세트에 대해 예측을 수행합니다. 이렇게 생성된 예측 값을 테스트 세트의 실제 결과(레이블)와 비교하여 정확도(accuracy), 정밀도(precision), 재현율(recall), F1-점수 등 문제 유형(분류 또는 회귀)에 따라 성능 지표를 평가합니다.

데이터 분할의 중요성

  • 과적합 방지 (Overfitting Prevention):

    • 별도의 테스트 세트를 보유함으로써, 모델이 단순히 훈련 데이터를 암기하는 것이 아니라 새로운 데이터에도 일반화할 수 있음을 확인할 수 있습니다.
    • 과적합은 모델이 훈련 데이터에서는 매우 우수하게 작동하지만, 새로운 데이터에서는 성능이 떨어질 때 발생합니다.
  • 모델 평가: 테스트 세트는 실제 환경에서 모델 성능을 평가할 수 있는 대안을 제공합니다.

  • 편향 감소 : 데이터를 훈련 세트와 테스트 세트로 분리하면 편향을 줄일 수 있습니다. 이렇게 하면 모델이 학습한 데이터와 다른 데이터 세트에서 평가되도록 보장됩니다.

데이터 분할 방법

  • Random Splitting: 코드에서 보듯이 데이터 세트를 무작위로 훈련 세트와 테스트 세트로 나누는 것이 일반적입니다.

  • 층화 분할 (Stratified Splitting): 데이터 세트가 불균형(예: 특정 클래스가 과소표현된 경우)한 상황에서는, 층화 샘플링을 통해 훈련 세트와 테스트 세트가 원래 데이터의 각 클래스 비율과 유사하도록 만듭니다.

  • 교차 검증 (Cross-Validation): 단순한 훈련-테스트 분할을 넘어, 데이터 세트를 더 견고하게 평가하기 위해 교차 검증(k-겹 교차 검증)과 같은 기법이 사용됩니다. 데이터 세트를 ’k’개의 하위 집합으로 나눈 후, 각 하위 집합을 번갈아 테스트 세트로 사용하고 나머지 하위 집합은 훈련에 사용합니다. 이 과정을 총 ’k’번 반복하며, 모델의 성능에 대한 종합적인 평가를 제공합니다.


다음 코드는 데이터 세트를 훈련용과 테스트용 하위 집합으로 나누는 단계입니다.

library(caret)
set.seed(2023)
mm1 = mm %>% 
  select(phqsum, Gender, Education, Age, Income, working_h)
trainingIndex <- createDataPartition(mm1$phqsum, p = .8, list = FALSE)
train_data <- mm1[+trainingIndex, ]
test_data  <- mm1[-trainingIndex, ]

caret 패키지 불러오기:

  • caret 패키지는 R에서 기계 학습 모델을 구축하기 위한 종합적인 프레임워크이며, 이 패키지를 로드합니다.

재현성을 위한 시드 설정:

  • set.seed(2023)은 랜덤 프로세스를 복제 가능하게 만들어줍니다.

데이터 준비:

  • mm1은 mm 데이터 세트에서 특정 열을 선택하여 생성됩니다. 이 열에는 PHQ-9 합계(phqsum)와 기타 인구통계학적 변수가 포함됩니다.

데이터 세트 분할:

  • createDataPartition: caret 패키지의 이 함수는 데이터 세트를 분할하기 위한 인덱스를 생성합니다. 이 함수는 phqsum 열을 기준으로 데이터를 훈련(80%)과 테스트(20%) 세트로 나눕니다.
  • trainingIndex: 훈련 세트의 행 인덱스를 저장합니다. train_data와 test_data: 원래의 mm1 데이터 세트는 이 인덱스를 사용하여 훈련 및 테스트 하위 집합으로 나뉩니다.

20.2.2 선형 회귀 (Linear Regression)

lm()을 사용한 모델 생성::

  • lm(data=., formula = phqsum ~ .): 이 코드는 선형 회귀 모델을 정의합니다. lm() 함수는 선형 모델을 적합(fit)시키는 데 사용됩니다.
  • data=.: 점(.)은 모델에 사용할 데이터가 현재 파이프라인의 데이터 세트임을 나타냅니다. 이 경우 train_data가 해당됩니다.
  • formula = phqsum ~ .: 이 공식은 phqsum이 종속 변수(예측할 변수)임을 지정하며, 틸드(~) 뒤의 점(.)은 train_data의 모든 다른 열이 독립 변수(예측 변수)로 사용된다는 것을 의미합니다.
model <- train_data %>%
  lm(data=., 
     formula =  phqsum ~ . )
summary(model)

Call:
lm(formula = phqsum ~ ., data = .)

Residuals:
   Min     1Q Median     3Q    Max 
-8.922 -2.243 -0.246  2.024 14.358 

Coefficients:
                    Estimate Std. Error t value Pr(>|t|)    
(Intercept)       -2.1405458  0.2658471  -8.052 9.34e-16 ***
GenderMale        -1.2060166  0.0703062 -17.154  < 2e-16 ***
Education1.High    0.4247050  0.0860791   4.934 8.22e-07 ***
Education2.Middle  0.8875284  0.0864575  10.265  < 2e-16 ***
Age                0.1144818  0.0029737  38.498  < 2e-16 ***
Income             0.0002444  0.0001221   2.002   0.0454 *  
working_h          0.0904169  0.0039128  23.108  < 2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 3.143 on 7995 degrees of freedom
Multiple R-squared:  0.2317,    Adjusted R-squared:  0.2312 
F-statistic: 401.9 on 6 and 7995 DF,  p-value: < 2.2e-16

단계 3

훈련 데이터에 선형 회귀 모델을 적합한 후, 지도 학습에서 다음으로 중요한 단계는 모델의 성능을 평가하는 것입니다. 이는 일반적으로 별도의 테스트 데이터 세트에 대해 예측을 수행한 다음, 오차 지표를 계산하여 이루어집니다. 제공된 R 코드를 다음과 같이 수행합니다:

predictions <- predict(model, test_data)
  • predict(model, test_data): 이 함수는 훈련된 모델(선형 회귀 모델)을 사용하여 예측을 수행하는 데 사용됩니다. 모델을 테스트 데이터(test_data)에 적용하여 결과를 예측합니다.
  • predictions: predict 함수의 출력으로, 테스트 데이터 세트의 각 관측치에 대해 종속 변수(phqsum)의 예측 값을 포함합니다.

Calculating Error Meatrix

  • Mean Squared Error (MSE):
    • mse[[“linear”]]: MSE는 실제 값과 예측 값 간의 평균 제곱 차이를 측정하는 지표입니다. 이는 (predictions - test_data$phqsum)^2 를 계산한 후 평균을 구하여 산출됩니다.
    • MSE 값이 낮을수록 모델이 데이터에 더 잘 적합(fit)되었음을 나타냅니다.
  • Root Mean Squared Error (RMSE):
    • rmse[[“linear”]]: RMSE는 MSE의 제곱근 값으로, 오차를 종속 변수와 동일한 단위로 표현하기 때문에 해석이 용이한 지표입니다.
    • MSE와 마찬가지로 RMSE 값이 낮을수록 모델 적합도가 더 좋음을 의미합니다.
mse = list()
rmse= list()
mse[["linear"]] <- mean((predictions - test_data$phqsum)^2)
rmse[["linear"]] <- sqrt(mse[["linear"]])
mse
$linear
[1] 9.967748
rmse
$linear
[1] 3.157174

20.2.3 랜덤 포레스트 (Random Forest)

랜덤 포레스트는 여러 개의 의사결정 나무(Decision Trees)를 활용하여 예측을 수행하는 강력한 기계 학습 방법입니다. 특히 복잡한 구조를 가진 대규모 데이터 세트를 처리하는 데 유용합니다. 이 챕터에서는 R에서 랜덤 포레스트 모델을 구현하고, 성능을 평가하며, 다른 모델과 비교하는 과정을 안내합니다.

랜덤 포레스트란 무엇인가?

  • 앙상블 학습 방법 (Ensemble Learning Method): 랜덤 포레스트는 앙상블 학습 기법 중 하나입니다. 앙상블 학습은 여러 학습 알고리즘을 결합하여 단일 알고리즘으로 얻을 수 있는 성능보다 더 나은 예측 성능을 얻는 방법입니다.

  • 의사결정 나무 기반 (Based on Decision Trees): 랜덤 포레스트는 여러 개의 의사결정 나무를 생성하고, 이들을 결합하여 더 정확하고 안정적인 예측을 제공합니다. 각 나무는 훈련 세트에서 대체 추출(bootstrap sample)한 데이터 샘플로 만들어집니다.

  • 과적합 방지 (Handling Overfitting): 기계 학습에서 가장 큰 문제 중 하나는 과적합(Overfitting)이지만, 랜덤 포레스트는 여러 나무의 결과를 평균하거나 결합하는 방식으로 과적합 위험을 줄여줍니다.

랜덤 포레스트는 어떻게 작동하는가?

  • 특성의 랜덤 선택 (Random Selection of Features): 각 의사결정 나무를 생성할 때, 랜덤 포레스트는 각 분할(Split)에서 사용할 특성(feature)의 일부만 무작위로 선택합니다. 이는 단일 의사결정 나무와 비교했을 때 모델에 추가적인 랜덤성을 부여합니다.

  • 다수의 나무 생성 (Creating Multiple Trees): 랜덤 포레스트는 여러 나무를 생성하며, 각 나무는 약간씩 다른 구조를 가집니다. 예측 시, 랜덤 포레스트는 각각의 의사결정 나무에서 나온 결과를 평균 내거나 다수결을 통해 최종 예측을 제공합니다.

  • 장점 (Advantages): 결과를 평균하거나 결합하는 과정은 정확도를 높이고 과적합을 제어하는 데 도움을 줍니다. 랜덤 포레스트는 회귀와 분류 작업 모두에서 사용할 수 있으며, 범주형 변수와 연속형 변수를 모두 잘 처리합니다.

  • 결측값 처리 (Handling Missing Values) : 랜덤 포레스트는 결측값을 자체적으로 보완(impute)하여 처리할 수 있습니다.

  • 변수 중요도 (Variable Importance) : 랜덤 포레스트는 예측에서 각 특성이 얼마나 중요한지를 직관적으로 나타내는 지표를 제공합니다.

병렬 컴퓨팅 설정 (Setting Up for Parallel Computing)

# Register the parallel backend
numCores <- parallel::detectCores()
registerDoParallel(cores = numCores - 1)
  • 병렬 처리 (Parallel Computing): 특히 랜덤 포레스트와 같은 복잡한 모델의 계산 속도를 높이기 위해 병렬 처리를 사용합니다.
  • parallel::detectCores(): 사용 중인 컴퓨터의 CPU 코어 수를 감지합니다.
  • registerDoParallel(cores = numCores - 1): 병렬 처리에 사용할 코어 수를 등록합니다. 한 개의 코어는 다른 작업을 위해 남겨둡니다.

Configuring the Training Process

train_control <- trainControl(method = "repeatedcv", 
                              number = 10, 
                              repeats = 3, 
                              allowParallel = TRUE)

Repeated Cross-Validation: 교차 검증은 통계 분석 결과가 독립적인 데이터 세트에 어떻게 일반화될지를 평가하기 위해 사용되는 기법입니다. 명시적인 검증 세트를 사용할 수 없는 경우, 가상의 검증 세트에 모델의 적합도를 예측하는 데 주로 사용됩니다.

10-겹 교차 검증이란

  • 정의: 데이터 세트를 무작위로 10개의 하위 집합(‘겹’ 또는 ‘fold’)으로 나누는 방법입니다. 각 하위 집합은 데이터 샘플의 대략 동일한 비율을 포함합니다.

  • 과정:

    • 매 라운드에서, 10개의 하위 집합 중 하나를 검증 세트로 사용하고, 나머지 아홉 개의 하위 집합을 훈련 세트로 결합하여 사용합니다.
    • 이 과정을 총 10번 반복하며, 매번 다른 하위 집합을 검증 세트로 사용합니다.
    • 10번의 결과를 평균(또는 다른 방식으로 결합)하여 최종 평가 값을 도출합니다.

왜10-Fold Cross-Validation을 사용하는가?

  • 편향 감소: 서로 다른 하위 집합을 검증 세트로 사용할 수 있으므로, 데이터 선택에 따른 편향을 줄일 수 있습니다.
  • 분산 분석: 훈련 데이터에 따른 모델 예측의 변화를 평가할 수 있어 모델 성능에 대한 종합적인 이해를 제공합니다.
  • 일반화 성능 확인: 데이터의 다양한 부분을 테스트함으로써 모델이 새로운 데이터에서도 잘 작동할 가능성을 평가합니다.

Repeated Cross-Validation

  • 이번 경우에서는 10-겹 교차 검증 과정을 3번 반복합니다. 이는 원본 데이터 세트를 10개의 겹(fold)으로 나눈 후, 매번 다른 무작위 분할로 10-겹 교차 검증을 수행한다는 것을 의미합니다.
  • 이러한 반복은 모델 성능 추정의 변동성을 더 줄이는 데 도움을 주며, 모델이 새로운 데이터에서도 얼마나 잘 작동할지를 더 견고하게 이해할 수 있도록 해줍니다.

10-fold cross-validation은 모델 성능을 평가하는 신뢰할 수 있는 방법입니다. 이를 반복적으로 사용하면 모델 평가의 견고성과 신뢰성을 크게 높일 수 있으며, 이를 통해 모델이 현재 데이터에만 적합한 것이 아니라 새로운 데이터에서도 잘 작동한다는 것을 보장할 수 있습니다.

랜덤 포레스트 모델 훈련 (Training the Random Forest Model)

  • Model Formula: phqsum ~ . 은 train_data의 모든 변수를 사용하여 phqsum을 예측하도록 지정합니다.

  • 랜덤 포레스트 알고리즘 지정: R 코드에서 method = “rf”는 랜덤 포레스트 알고리즘을 사용할 것을 지정합니다.

tic()
model_rf <- caret::train(phqsum ~ ., data = train_data, 
                  method = "rf",
                  trControl = train_control)  
toc()

랜덤 포레스트 모델 성능 평가: 예측 및 오류 지표 (Evaluating Random Forest Model Performance: Predictions and Error Metrics)

훈련 데이터(train_data)를 사용해 랜덤 포레스트 모델(model_rf)을 학습한 후에는 성능 평가가 필수적입니다. 이 평가 과정은 별도의 테스트 데이터 세트에서 예측을 수행하고, 평균 제곱 오차(MSE)와 평균 제곱근 오차(RMSE)와 같은 오류 지표를 계산하여 이루어집니다.

  • 평균 제곱 오차 (MSE): 예측 값과 실제 값 간의 차이를 제곱하여 평균을 구한 값으로, 낮을수록 더 나은 모델을 의미합니다.
  • 평균 제곱근 오차 (RMSE): MSE의 제곱근 값으로, 종속 변수와 동일한 단위로 표현되기 때문에 해석이 용이합니다.

테스트 세트에서 예측 수행 Making Predictions on the Test Set

# Make predictions on the test set
predictions_rf <- predict(model_rf, test_data)
  • predict(model_rf, test_data): 훈련된 랜덤 포레스트 모델(model_rf)을 사용하여 테스트 데이터(test_data)를 기반으로 결과를 예측합니다.
  • predictions_rf: predict 함수의 결과로, 테스트 데이터 세트의 각 관측치에 대한 종속 변수(phqsum)의 예측 값을 포함합니다.

Calculating MSE and RMSE

# Calculate MSE and RMSE
mse[["rf"]] <- mean((predictions_rf - test_data$phqsum)^2)
rmse[["rf"]] <- sqrt(mse[["rf"]])
mse
$linear
[1] 9.967748

$rf
[1] 9.878868
rmse
$linear
[1] 3.157174

$rf
[1] 3.143067

20.2.4 일반화 가법 모델 (Generalized Additive Model, GAM)

일반화 가법 모델(GAM)은 통계 및 기계 학습에서 사용되는 유연한 모델 클래스입니다. 선형 모델을 확장하여 예측 변수와 결과 간의 비선형 관계를 허용하면서도 해석 가능성을 유지합니다.

Setting Up the Environment

if(!require("mgcv")) install.packages("mgcv")
library(mgcv)
names(train_data)
[1] "phqsum"    "Gender"    "Education" "Age"       "Income"    "working_h"

Training the GAM

  • gam Function: mgcv 패키지의 gam 함수는 GAM을 적합(fit)하는 데 사용됩니다.
  • Model Formula: phqsum ~ s(Age) + Gender + Education + s(Income) + s(working_h) 는 모델의 구조를 나타냅니다.
  • s():스무스(smooth) 항목으로, 예측 변수와 결과 간의 비선형 관계를 허용합니다.
  • Gender, Education: 선형 예측 변수로 포함됩니다.
  • Data: data=. 은 모델이 train_data 데이터를 사용한다는 것을 나타냅니
model_gam = train_data %>%
  gam(data=., 
      phqsum ~ s(Age) + Gender + Education + s(Income) + s(working_h))

예측 및 성능 평가 (Making Predictions and Evaluating Performance)

predictions_gam <- predict(model_gam, newdata=test_data)
mse[["gam"]] <- mean((predictions_gam - test_data$phqsum)^2)
rmse[["gam"]] <- sqrt(mse[["gam"]])
mse
$linear
[1] 9.967748

$rf
[1] 9.878868

$gam
[1] 9.66441
rmse
$linear
[1] 3.157174

$rf
[1] 3.143067

$gam
[1] 3.108763
  • 예측 수행: predict 함수를 사용하여 GAM 모델(model_gam)을 테스트 데이터(test_data)에 적용하여 예측 값을 생성합니다.
  • MSE 계산: 테스트 세트에서 예측 값과 실제 값 간의 평균 제곱 오차를 계산합니다.
  • RMSE 계산: MSE의 제곱근을 구해 평균 제곱근 오차를 계산합니다.

20.2.5 Ridge and Lasso Regression

릿지 회귀와 라소 회귀는 기계 학습 및 통계에서 널리 사용되는 규제화(Regularization) 기법입니다. 이 기법들은 특히 다중공선성(Multicollinearity) 문제를 다룰 때나 독립 변수(특징)가 관측치보다 많을 때 유용합니다. 두 방법 모두 제곱 최소화 목적 함수(Least Squares Objective Function)에 패널티 항을 추가하여 과적합(Overfitting)을 제어합니다.

환경 설정 및 데이터 준비 (Setting Up the Environment and Preparing Data)

if(!require("glmnet")) install.packages("glmnet")
library(glmnet)
  • glmnet Package: 일반화된 선형 모델을 패널티 최대 우도(Penalized Maximum Likelihood)를 통해 적합시키는 데 필수적인 패키지로, 설치 후 로드합니다.
# Convert categorical variables to dummy variables
train_data_matrix <- model.matrix(phqsum ~ ., data = train_data)[, -1]  # Exclude intercept column
test_data_matrix <- model.matrix(phqsum ~ ., data = test_data)[, -1]
  • Data Preparation: model.matrix 함수는 범주형 변수를 회귀 분석을 위한 더미 변수(Indicator Variables)로 변환합니다. 상수항(Intercept) 열은 제외합니다.
# Define the response variable
y_train <- train_data$phqsum
y_test <- test_data$phqsum
  • Response Variable: 훈련 데이터와 테스트 데이터에서 종속 변수(phqsum)를 추출하여 별도의 변수로 저장합니다.

Fitting Ridge and Lasso Regression Models

# Ridge Regression
model_ridge <- glmnet(train_data_matrix, y_train, alpha = 0)
# Lasso Regression
model_lasso <- glmnet(train_data_matrix, y_train, alpha = 1)
  • Ridge Regression (alpha = 0): 계수 크기의 제곱을 패널티로 추가하여 과적합을 제어합니다.
  • Lasso Regression (alpha = 1): 계수 크기의 절대값을 패널티로 추가하며, 이는 일부 계수를 0으로 만들어 변수 선택 효과를 제공합니다.

Selecting the Best Lambda (Penalty Parameter)

set.seed(123)  # For reproducibility
cv_ridge <- cv.glmnet(train_data_matrix, y_train, alpha = 0)
cv_lasso <- cv.glmnet(train_data_matrix, y_train, alpha = 1)
best_lambda_ridge <- cv_ridge$lambda.min
best_lambda_lasso <- cv_lasso$lambda.min
  • 교차 검증 (Cross-Validation): 각 모델에 대해 최적의 람다(패널티 매개변수)를 결정합니다. - cv.glmnet: 릿지 및 라소 회귀 모델에 대한 교차 검증을 수행합니다. - lambda.min: 교차 검증 결과 가장 낮은 오차를 제공하는 최적의 람다 값입니다.

Making Predictions and Evaluating Models

# Predictions
predictions_ridge <- predict(model_ridge, s = best_lambda_ridge, newx = test_data_matrix)
predictions_lasso <- predict(model_lasso, s = best_lambda_lasso, newx = test_data_matrix)

Choosing Between Ridge and Lasso

  • Predictive Performance: 데이터와 문제 유형에 따라 릿지와 라소 중 어느 것이 더 나은지 선택합니다.
  • Cross-Validation: 교차 검증을 통해 최적의 람다 값을 찾는 것은 두 방법 모두에서 편향과 분산 간의 균형을 맞추는 데 중요합니다.
# Evaluation
mse[["ridge"]] <- mean((predictions_ridge - y_test)^2)
rmse[["ridge"]] <- sqrt(mse[["ridge"]])
mse[["lasso"]] <- mean((predictions_lasso - y_test)^2)
rmse[["lasso"]] <- sqrt(mse[["lasso"]])
do.call(cbind, list(mse, rmse)) %>% data.frame() %>%
  setNames(c("mse", "rmse"))
            mse     rmse
linear 9.967748 3.157174
rf     9.878868 3.143067
gam     9.66441 3.108763
ridge  9.974757 3.158284
lasso  9.966269  3.15694

20.3 분류

20.3.1 로지스틱 회귀 (Logistic Regression)

Step 1: 라이브러리 로드 및 시드 설정 (Load Libraries and Set Seed)

이 단계에서는 기계 학습 모델을 훈련하고 평가하기 위한 함수들을 제공하는 caret 라이브러리를 로드합니다. 또한, 결과의 재현성을 보장하기 위해 난수 생성 시드를 설정합니다.

# Import necessary libraries
library(caret)
# Set a random seed for reproducibility
set.seed(2023)

Step 2: 데이터 준비 (Data Preparation)

이 단계에서는 데이터를 준비합니다. 먼저, 데이터 세트에서 타겟 변수(depressive)와 관련 있는 변수들(Gender, Education, Age, Income, working_h)을 선택합니다. 그 후, createDataPartition 함수를 사용해 데이터를 훈련 세트(train_data)와 테스트 세트(test_data)로 분할합니다. 이렇게 하면 모델 훈련과 평가를 위한 별도의 데이터 세트를 확보할 수 있습니다. - createDataPartition 함수: 데이터에서 타겟 변수(depressive)의 분포를 유지하면서 훈련 세트와 테스트 세트를 분할합니다. 이는 데이터의 클래스 불균형 문제를 완화하는 데 유용합니다.

# Select relevant variables, including the target variable
mm1 <- mm %>%
  select(depressive, Gender, Education, Age, Income, working_h)
# Split the data into training and testing sets
trainingIndex <- createDataPartition(mm1$depressive, p = 0.8, list = FALSE)
train_data <- mm1[+trainingIndex, ]
test_data  <- mm1[-trainingIndex, ]

Step 3: Model Training - Logistic Regression

이 단계에서는 glm 함수를 사용해 로지스틱 회귀 모델을 훈련합니다. 이 모델은 독립 변수(Gender, Education, Age, Income, working_h)를 기반으로 관측치가 “Depressive”인지 “None”인지 예측합니다. 로지스틱 회귀는 각 클래스의 확률을 추정하며, 이를 통해 이진 분류를 수행합니다.

# Fit a logistic regression model
model_logistic <- train_data %>%
  glm(data=., family=binomial(), 
      formula = depressive == "Depressive" ~ .)
  • glm 함수: family=binomial() 옵션을 사용해 로지스틱 회귀 모델을 지정합니다.로지스틱 회귀는 종속 변수(타겟 변수)가 두 개의 범주를 가지는 경우에 적합한 모델입니다.
  • depressive == “Depressive”: 타겟 변수에서 “Depressive” 클래스에 속할 확률을 모델이 학습하도록 설정합니다.

Step 4: Model Evaluation

# Make predictions
predictions_logistic_prob = predict(model_logistic, newdata = test_data, type = "response")
predictions_logistic      = ifelse(predictions_logistic_prob > 0.5, "Depressive", "None")

# Calculate and store balanced accuracy
smry_logi = confusionMatrix(predictions_logistic %>% as.factor(), 
                test_data$depressive %>% as.factor())
bacu <- list()
bacu[["logistic"]] <- smry_logi$byClass[["Balanced Accuracy"]]
  • 예측 값 생성: predict 함수로 테스트 세트(test_data)의 관측치에 대해 예측 수행. type = “response” 옵션을 사용해 “Depressive” 클래스의 확률값을 반환. 확률 값이 0.5 이상이면 “Depressive”, 그렇지 않으면 “None”으로 분류.

  • 혼동 행렬 계산: confusionMatrix 함수로 실제 값과 예측 값의 분류 성능을 비교. 민감도(Sensitivity)와 특이도(Specificity)를 통해 모델의 균형 정확도(Balanced Accuracy) 계산.

  • 결과 저장: 균형 정확도는 bacu 리스트의 “logistic” 키에 저장.

혼동 행렬 (Confusion Matrix)

민감도 (Sensitivity) - 실제 “Depressive” 클래스 중 올바르게 예측된 비율.

\[ \text{Sensitivity} = \frac{\text{True Positives}}{\text{True Positives} + \text{False Negatives}} \]

특이도 (Specificity) - 실제 “None” 클래스 중 올바르게 예측된 비율.

\[ \text{Specificity} = \frac{\text{True Negatives}}{\text{True Negatives} + \text{False Positives}} \]

균형 정확도 (Balanced Accuracy) - 민감도와 특이도의 평균으로, 클래스 불균형이 있는 데이터에서 모델 성능을 공정하게 평가하는 지표.

\[ \text{Balanced Accuracy} = \frac{\text{Sensitivity} + \text{Specificity}}{2} \]

20.3.2 random forest

Step 1: Register the Parallel Backend

# Register the parallel backend to speed up computations
numCores <- parallel::detectCores()
registerDoParallel(cores = numCores - 1)

이 단계에서는 병렬 처리를 구성하여 여러 CPU 코어를 활용함으로써 모델 훈련 속도를 높입니다. - parallel::detectCores: 사용 가능한 CPU 코어의 수를 감지합니다. - registerDoParallel(cores = numCores - 1): 병렬 처리를 설정하며, 시스템 작업을 위해 한 개의 코어를 남겨둡니다.

Step 2: Define Control Parameters for Training

# Define control parameters for the training process
train_control <- trainControl(method = "repeatedcv", 
                              number = 10, 
                              repeats = 3, 
                              classProbs = TRUE,
                              allowParallel = TRUE)

랜덤 포레스트 모델의 훈련 과정을 제어하는 매개변수를 정의합니다. - method = “repeatedcv”: 반복 교차 검증(Repeated Cross-Validation)을 리샘플링 방법으로 사용합니다. - number = 10: 10-겹 교차 검증을 설정합니다. - repeats = 3: 교차 검증 과정을 3번 반복합니다. - classProbs = TRUE: 모델이 클래스 확률을 계산하도록 허용합니다. - allowParallel = TRUE: 교차 검증에서 병렬 처리를 활성화합니다.

Step 3: Train the Random Forest Model

# Train the Random Forest model
tic()  # Start measuring time
model_rf_class <- caret::train(depressive ~ ., data = train_data, 
                        method = "rf",
                        trControl = train_control)
toc()  # Stop measuring time
  • 이 단계에서는 랜덤 포레스트 분류 모델을 훈련합니다.
    • depressive ~ . 종속 변수(depressive)를 훈련 데이터(train_data)의 나머지 모든 변수들을 사용하여 예측하도록 설정합니다.
    • method = “rf”: 랜덤 포레스트 알고리즘을 지정합니다.
    • trControl = train_control: 이전에 정의한 제어 매개변수를 사용해 훈련 과정을 설정합니다.
    • tic() 및 toc(): 훈련에 소요된 시간을 측정합니다.

Step 4: Make Predictions and Evaluate the Model

# Make predictions using the trained model
prediction_rf_class = predict(model_rf_class, newdata = test_data)
# Calculate the confusion matrix and balanced accuracy
smry_rf = confusionMatrix(prediction_rf_class %>% as.factor(), 
                         test_data$depressive %>% as.factor())
# Store the balanced accuracy in the 'bacu' list
bacu[["rf"]] = smry_rf$byClass[["Balanced Accuracy"]]

평가 절차: - 예측 수행:훈련된 랜덤 포레스트 모델(model_rf_class)을 사용해 테스트 데이터(test_data)에서 타겟 변수(depressive)를 예측합니다. - 혼동 행렬 계산: confusionMatrix 함수로 실제 값과 예측 값을 비교하여 모델의 분류 성능을 평가합니다. 혼동 행렬은 정확도, 민감도(Sensitivity), 특이도(Specificity) 등 다양한 성능 지표를 제공합니다.

- 균형 정확도 저장: 혼동 행렬에서 균형 정확도(Balanced Accuracy)를 추출하여 리스트 bacu에 "rf" 키로 저장합니다.

20.4 Caret Package

caret 페키지를 이용해서 여러 가징 모형을 수행해 봅니다. https://topepo.github.io/caret/

Caret(Classification And REgression Training) 패키지는 R에서 기계 학습 모델을 훈련하고 평가하기 위한 통합된 프레임워크를 제공합니다. 이 패키지는 데이터 전처리, 모델 훈련, 하이퍼파라미터 튜닝, 모델 평가 등 전 과정을 간단하고 효율적으로 수행할 수 있도록 설계되었습니다.

Caret의 주요 특징 - 다양한 알고리즘 지원 - caret 패키지는 약 200개 이상의 기계 학습 알고리즘을 지원합니다. - 모델링 알고리즘은 기본적으로 R의 다른 패키지와 연결되어 있어, caret 인터페이스만으로 다양한 알고리즘을 사용할 수 있습니다.

  • 통합된 워크플로
    • 데이터 전처리, 모델 훈련, 튜닝, 평가를 하나의 통합된 워크플로로 처리할 수 있습니다.
    • 이 패키지를 사용하면 모델 간 비교가 간단해지며, 같은 코드 구조를 통해 다양한 알고리즘을 실행할 수 있습니다.
  • 모델 튜닝 및 교차 검증
    • caret은 자동화된 하이퍼파라미터 튜닝과 교차 검증을 제공합니다.
    • 여러 하이퍼파라미터를 체계적으로 탐색하며 모델 성능을 최적화할 수 있습니다.
  • 성능 평가
    • 혼동 행렬, ROC 곡선, 정확도 등 다양한 성능 평가 메트릭스를 제공합니다.
  • 데이터 전처리
    • 결측치 처리, 변수 변환, 스케일링, 표준화 등 데이터 전처리 도구를 제공합니다.
  • Caret의 주요 함수
    • train() : 모델을 훈련시키는 핵심 함수. 데이터를 기반으로 다양한 기계 학습 알고리즘을 학습시킵니다. 교차 검증 및 하이퍼파라미터 튜닝을 포함하여 성능을 최적화합니다.
    • trainControl() : 모델 훈련 과정의 매개변수를 설정. 교차 검증 방법, 반복 횟수, 샘플링 방식 등을 정의합니다.
    • preProcess() : 데이터 전처리를 수행하는 함수. 결측값 대체, 스케일링, 로그 변환 등을 제공합니다.
    • confusionMatrix() : 분류 모델의 예측 결과를 평가. 정확도, 민감도, 특이도 등 주요 성능 지표를 계산합니다.
    • predict() : 훈련된 모델을 사용하여 새로운 데이터에 대한 예측을 수행.

20.4.1 Gradient Boosting Machine (GBM) classification

if(!require("gbm")) install.packages("gbm")

tic()
model_gbm_class <- caret::train(depressive ~ ., data = train_data, 
                        method = "gbm",
                        trControl = train_control)  
Iter   TrainDeviance   ValidDeviance   StepSize   Improve
     1        1.2461            -nan     0.1000    0.0073
     2        1.2340            -nan     0.1000    0.0058
     3        1.2229            -nan     0.1000    0.0055
     4        1.2136            -nan     0.1000    0.0044
     5        1.2066            -nan     0.1000    0.0034
     6        1.2002            -nan     0.1000    0.0030
     7        1.1939            -nan     0.1000    0.0029
     8        1.1889            -nan     0.1000    0.0023
     9        1.1834            -nan     0.1000    0.0024
    10        1.1793            -nan     0.1000    0.0018
    20        1.1475            -nan     0.1000    0.0013
    40        1.1201            -nan     0.1000    0.0002
    60        1.1098            -nan     0.1000    0.0001
    80        1.1054            -nan     0.1000   -0.0000
   100        1.1033            -nan     0.1000   -0.0000
   120        1.1021            -nan     0.1000   -0.0000
   140        1.1011            -nan     0.1000   -0.0001
   150        1.1007            -nan     0.1000   -0.0001
toc()
1.424 sec elapsed
prediction_gbm_class = predict(model_gbm_class, newdata = test_data)
smry_gbm = confusionMatrix(prediction_gbm_class %>% as.factor(), 
                          test_data$depressive %>% as.factor())
bacu[["gbm"]] = smry_gbm$byClass[["Balanced Accuracy"]]

20.4.2 svmRadial

if(!require("kernlab")) install.packages("kernlab")

tic()
model_svmRadial_class <- caret::train(depressive ~ ., data = train_data, 
                         method = "svmRadial",
                         trControl = train_control)  
toc()
23.77 sec elapsed
prediction_svmRadial_class = predict(model_svmRadial_class, newdata = test_data)
smry_svmRadial = confusionMatrix(prediction_svmRadial_class %>% as.factor(), 
                           test_data$depressive %>% as.factor())
bacu[["svmRadial"]] = smry_svmRadial$byClass[["Balanced Accuracy"]]

20.4.3 knn

if(!require("rpart")) install.packages("rpart")
tic()
model_tree_class <- caret::train(depressive ~ ., data = train_data, 
                               method = "rpart",
                               trControl = train_control)  
toc()
0.775 sec elapsed
prediction_tree_class = predict(model_tree_class, newdata = test_data)
smry_tree = confusionMatrix(prediction_tree_class %>% as.factor(), 
                                 test_data$depressive %>% as.factor())
bacu[["tree"]] = smry_tree$byClass[["Balanced Accuracy"]]

20.4.4 final model comparision

bacu
$logistic
[1] 0.6275994

$rf
[1] 0.6238929

$gbm
[1] 0.6374802

$svmRadial
[1] 0.597726

$tree
[1] 0.6180259