1 보험금 청구

스마트폰의 보급과 기술의 발달로 인해 보험회사들의 보험금청구 간소화 노력이 빛을 발하고 있다. 더불어 보험금 사기 청구도 늘어나고 있어 빠른 시간내에 사기 보험금 청구를 걸러내고 정상적인 보험금 지급이 빨리 이루어질 수 있도록 조치가 필요하다.

2 보험금 청구 데이터 1

보험청구사기는 미국에서 한해 $30 billion 달러로 추산된다. 국내에서도 보험사기 규모가 연간 4.5조에 달할 것이라고 한다. 2

보험사기 데이터를 다운로드 받아 보험사기 예측모형을 구축한다. 먼저 보험사기로 밝혀진 사례가 전체 1000개 중 몇건인지 살펴본다.

library(tidyverse)

ins_dat <- read_csv("data/insurance_claims.csv", na = "?")

ins_dat %>% 
  count(fraud_reported) %>% 
  mutate(pcnt = n /sum(n))
# A tibble: 2 x 3
  fraud_reported     n  pcnt
  <chr>          <int> <dbl>
1 N                753 0.753
2 Y                247 0.247

3 feature 공학

Vishal (February 11, 2019), “Insurance Claims Fraud Detection - Part 1” 블로그를 참조하여 불필요한 변수를 제거하고 바로 예측모형 구축으로 돌입한다.

변수 6개(‘policy_number’, ‘insured_zip’, ‘policy_bind_date’, ‘incident_date’, ‘incident_location’, ’_c39’, ‘auto_year’, ‘incident_hour_of_the_day’)는 특별한 의미가 없기 때문에 제거하고, 결측값도 property_damage, police_report_available, collision_type 있어 강건한 random forest 예측모형을 결측값 보완없이 사용하도록 한다. 그러면 갖가지 문제가 발생되어… 문제가 클 수 있으니 우선 NA 결측값을 “unknown”으로 채워넣도록 한다.

ins_df <- ins_dat %>% 
  select(-c('policy_number', 'insured_zip', 'policy_bind_date', 'incident_date', 'incident_location', '_c39', 'auto_year', 'incident_hour_of_the_day')) %>% 
  mutate(fraud_reported = factor(fraud_reported, levels=c("N", "Y"))) %>% 
  mutate(property_damage = ifelse(is.na(property_damage), "unknown", property_damage),
         police_report_available = ifelse(is.na(police_report_available), "unknown", police_report_available),
         collision_type = ifelse(is.na(collision_type), "unknown", collision_type)) %>% 
    mutate_if(is.character, as.factor)

ins_df %>% 
  gather(variable, value) %>% 
  mutate(na_value = is.na(value)) %>% 
  group_by(variable) %>% 
  summarise(na_pcnt = mean(na_value)) %>% 
  arrange(desc(na_pcnt))
# A tibble: 32 x 2
   variable              na_pcnt
   <chr>                   <dbl>
 1 age                         0
 2 authorities_contacted       0
 3 auto_make                   0
 4 auto_model                  0
 5 bodily_injuries             0
 6 capital-gains               0
 7 capital-loss                0
 8 collision_type              0
 9 fraud_reported              0
10 incident_city               0
# … with 22 more rows
ins_df %>% 
  count(collision_type)
# A tibble: 4 x 2
  collision_type      n
  <fct>           <int>
1 Front Collision   254
2 Rear Collision    292
3 Side Collision    276
4 unknown           178

4 예측모형 3

Random Forest와 GLM 로지스틱 회귀모형을 적합시켜서 성능을 살펴본다.

library(tidymodels)
library(parsnip)
library(rsample)

## 훈련/시험 데이터 분할 -----
basetable_split <- initial_split(ins_df, props = 6/10)

train_df <- training(basetable_split)
test_df  <- testing(basetable_split)

## Random Forest 예측모형 -----
rf_model <- rand_forest(trees = 1000, mode = "classification") %>%
  set_engine("randomForest", seed = 63233) %>% 
  fit(fraud_reported ~ ., data = train_df)

# 예측모형 성능평가
rf_prob  <- predict(rf_model, test_df, type="prob")
rf_class <- ifelse(rf_prob[,2] > 0.247, "Y", "N") %>% as.factor

caret::confusionMatrix(rf_class, test_df$fraud_reported)
Confusion Matrix and Statistics

          Reference
Prediction   N   Y
         N 138  10
         Y  37  65
                                         
               Accuracy : 0.812          
                 95% CI : (0.758, 0.8585)
    No Information Rate : 0.7            
    P-Value [Acc > NIR] : 3.848e-05      
                                         
                  Kappa : 0.5941         
 Mcnemar's Test P-Value : 0.0001491      
                                         
            Sensitivity : 0.7886         
            Specificity : 0.8667         
         Pos Pred Value : 0.9324         
         Neg Pred Value : 0.6373         
             Prevalence : 0.7000         
         Detection Rate : 0.5520         
   Detection Prevalence : 0.5920         
      Balanced Accuracy : 0.8276         
                                         
       'Positive' Class : N              
                                         
## GLM 예측모형 -----
glm_model <- logistic_reg() %>%
  set_engine("glm") %>% 
  fit(fraud_reported ~ ., data = train_df)

# 예측모형 성능평가
glm_prob  <- predict(glm_model, test_df, type="prob")
glm_class <- ifelse(glm_prob[,2] > 0.247, "Y", "N") %>% as.factor

caret::confusionMatrix(glm_class, test_df$fraud_reported)
Confusion Matrix and Statistics

          Reference
Prediction   N   Y
         N 151  26
         Y  24  49
                                         
               Accuracy : 0.8            
                 95% CI : (0.745, 0.8478)
    No Information Rate : 0.7            
    P-Value [Acc > NIR] : 0.0002317      
                                         
                  Kappa : 0.5202         
 Mcnemar's Test P-Value : 0.8875371      
                                         
            Sensitivity : 0.8629         
            Specificity : 0.6533         
         Pos Pred Value : 0.8531         
         Neg Pred Value : 0.6712         
             Prevalence : 0.7000         
         Detection Rate : 0.6040         
   Detection Prevalence : 0.7080         
      Balanced Accuracy : 0.7581         
                                         
       'Positive' Class : N