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