서포트 벡터 머신(support vector machine, SVM)은 기계 학습의 분야 중 하나로 패턴 인식, 자료 분석을 위한 지도 학습 모델이며, 주로 분류와 회귀 분석을 위해 사용한다. 두 카테고리 중 어느 하나에 속한 데이터의 집합이 주어졌을 때, SVM 알고리즘은 주어진 데이터 집합을 바탕으로 하여 새로운 데이터가 어느 카테고리에 속할지 판단하는 비확률적 이진 선형 분류 모델을 제작하는데 유용하고 회귀문제도 적용이 가능하다. 2
“An intuitive introduction to support vector machines using R – Part 1”에 공개된 데이터를 다운로드 받아 원본 데이터를 시각화한다.
# 0. 팩키지 -----
library(tidyverse)
library(readxl)
library(caret)
library(e1071)
library(extrafont)
loadfonts()
# 1. 데이터 -----
# download.file("https://eight2late.files.wordpress.com/2018/06/sugar_content.xls", destfile="data/sugar.xls", mode = "wb")
drink_df <- read_excel("data/sugar.xls")
# 2. 원본 데이터 -----
drink_df %>%
ggplot(aes(x=sugar_content, y=c(0))) +
geom_point() +
theme_bw(base_family = "NanumGothic") +
labs(x="설탕 함유량", y="") +
geom_text(data=drink_df, aes(label = sugar_content), size = 2.5, vjust=-2)
1차원 공간에 나온 설탕이 많이 함유된 제품과 설탕이 적게 함유된 제품을 구분하는 의사결정 경계(deicion boundary)를 시각화해서 다음과 같이 표현할 수 있다.
# 3. decision boundary -----
decision_bound_df <- tribble(
~sep,
9.1,
9.7
)
drink_df %>%
ggplot(aes(x=sugar_content, y=c(0))) +
geom_point() +
theme_bw(base_family = "NanumGothic") +
labs(x="설탕 함유량", y="") +
geom_text(data=drink_df, aes(label = sugar_content), size = 2.5, vjust=-2) +
geom_point(data=decision_bound_df, aes(x=sep, y=0), color="red", size=3) +
geom_text(data=decision_bound_df, aes(x=sep, y=0, label = sep), size = 2.5, vjust=-2)
1차원 공간에 나온 설탕이 많이 함유된 제품과 설탕이 적게 함유된 제품을 구분하는 의사결정 경계(deicion boundary)는 다수 존재하지만, 마진(margin)을 최대화하는 의사결정 선은 아마도 두 의사결정 설탕 함유량 데이터를 평균내는 것이 합리적일 적이다.
# 4. 마진(margin) -----
mm_sep_df <- tribble(
~sep,
(8.8+10)/2
)
drink_df %>%
ggplot(aes(x=sugar_content, y=c(0))) +
geom_point() +
theme_bw(base_family = "NanumGothic") +
labs(x="설탕 함유량", y="") +
geom_text(data=drink_df, aes(label = sugar_content), size = 2.5, vjust=-2) +
geom_point(data=decision_bound_df, aes(x=sep, y=0), color="red", size=3) +
geom_text(data=decision_bound_df, aes(x=sep, y=0, label = sep), size = 2.5, vjust=-2) +
geom_point(data=mm_sep_df, aes(x=sep, y=0), color="blue", size=3) +
geom_text(data=mm_sep_df, aes(x=sep, y=0, label = sep), size = 2.5, vjust=-2)
가로(0 – 1), 세로(0–1) 직사사각형 공간에 난수를 500개 생성한다. 그리고 이를 시각화한다.
# 1. 데이터 -----
n <- 500
two_df <- tibble(x_1=runif(n), x_2=runif(n))
# 2. 원본 데이터 -----
two_df %>%
ggplot(aes(x=x_1, y=x_2)) +
geom_point() +
theme_bw(base_family = "NanumGothic") +
labs(x="x_1", y="x_2") +
coord_fixed()
두개 집단으로 나누기 위해서 \(y = x\) 방정식을 통해 두개 삼각형 공간으로 나누고 각 집단을 구분하고 \(\delta\) 0.1로 설정하여 마진을 둔다.
# 3. 의사결정 경계(Decision Boundary) -----
two_df <- two_df %>%
mutate(y = as.factor(ifelse(x_1 > x_2, -1, 1)))
## 3.1. 의사결정 경계선과 마진 사이 비무장지대 설정
delta <- 0.1
two_df <- two_df %>%
filter(abs(x_1 - x_2) > delta)
## 3.2. 시각화
two_df %>%
ggplot(aes(x = x_1, y = x_2, color = y)) +
geom_point() +
theme_bw(base_family = "NanumGothic") +
labs(x="x_1", y="x_2") +
scale_color_manual(values = c("-1" = "red", "1" = "blue")) +
geom_abline(slope = 1, intercept = 0, size=1.5) +
coord_fixed() +
geom_abline(slope = 1, intercept=delta, linetype = "dashed") +
geom_abline(slope = 1, intercept=-delta, linetype = "dashed")
caret
팩키지를 사용해서 훈련/시험 데이터로 나누고 이를 e1071
팩키지 svm()
함수를 사용해서 분류모형을 구축한다.
svm()
모형을 구축할 때, type
, kernel
, scale
, cost
가 주요 모수가 되니, 주어진 데이터를 잘 분류할 수 있는 모형을 구축할 때 신중히 모수를 설정한다.
# 2. 훈련/시험 데이터 분할 ----
train_index <- createDataPartition(two_df$y, p = .8, list = FALSE)
train_df <- two_df[ train_index,]
test_df <- two_df[-train_index,]
# 3. SVM 모형 적합 -----
svm_model <- svm(y ~ ., data=train_df, type="C-classification", kernel="linear", scale=FALSE)
# 4. SVM 모형 평가 -----
## 4.1. 훈련데이터
pred_train <- predict(svm_model, train_df)
mean(pred_train == train_df$y)
[1] 1
## 4.2. 검증데이터
pred_test <- predict(svm_model, test_df)
mean(pred_test == test_df$y)
[1] 1
svm()
함수로 모형을 구축하고 R 객체로 저장하게 되면, svm_model$index
, svm_model$SV
을 통해 서포트 벡터와 이에 해당되는 데이터 행을 추출해낼 수 있다.
# 5. SVM 모형 시각화 -----
## 5.1. 지지벡터(Support Vector) 의사결정 경계점 시각화 -----
train_sv <- train_df[svm_model$index,]
train_df %>%
ggplot(aes(x = x_1, y = x_2, color = y)) +
geom_point() +
theme_bw(base_family = "NanumGothic") +
labs(x="x_1", y="x_2") +
scale_color_manual(values = c("-1" = "red", "1" = "blue")) +
geom_abline(slope = 1, intercept = 0, size=0.7, linetype=3, alpha=0.7) +
coord_fixed() +
geom_point(data=train_sv, aes(x=x_1, y=x_2), colour="purple", size = 4, alpha=0.5)
가중치를 계산하고 이를 반영하여 기울기와 절편을 계산하면 SVM 의사결정 경계선을 시각화할 수 있다.
## 5.2. 지지벡터(Support Vector) 시각화 -----
weight <- t(svm_model$coefs) %*% svm_model$SV
### 기울기
slope_1 <- -weight[1]/weight[2]
### 절편
intercept_1 <- svm_model$rho/weight[2]
train_df %>%
ggplot(aes(x = x_1, y = x_2, color = y)) +
geom_point() +
theme_bw(base_family = "NanumGothic") +
labs(x="x_1", y="x_2") +
scale_color_manual(values = c("-1" = "red", "1" = "blue")) +
coord_fixed() +
geom_point(data=train_sv, aes(x=x_1, y=x_2), colour="purple", size = 4, alpha=0.5) +
geom_abline(slope=slope_1, intercept = intercept_1) +
geom_abline(slope=slope_1,intercept = intercept_1-1/weight[2], linetype="dashed")+
geom_abline(slope=slope_1,intercept = intercept_1+1/weight[2], linetype="dashed")
SVM 내장된 시각화함수를 통하면 앞선 작업없이 시각화가 가능하다. \(\times\)는 서포트 벡터를 나타내고 분류결과를 채색하여 시각적으로 표현해 준다.
## 5.3. SVM 자체 내장 시각화 -----
plot(x=svm_model, data=train_df)
선형 SVM을 통해 두집단을 분류하는 문제에 적용시켰다. iris
데이터와 같이 집단이 3개를 분류할 경우 적용하는 전략은 one-against-one
분류전략이 된다.
# 1. 데이터 -----
data(iris)
## 1.1. 데이터 시각화 ----
iris %>%
ggplot(aes(x=Petal.Width,y=Petal.Length,colour=Species)) +
geom_point() +
theme_minimal(base_family="NanumGothic")
iris
데이터를 한번만 훈련/시험 데이터로 나눠 분류모형을 작업하게 되면 일반화에 문제가 생길수가 있으니 100회 반복하여 분류작업을 수행한다.
수행결과를 평균내고 이를 수치화한다.
# 2. one-against-one 분류전략 -----
## 2.1. 훈련/시험 데이터 분할 ----
train_index <- createDataPartition(iris$Species, p = .8, list = FALSE)
train_df <- iris[ train_index,]
test_df <- iris[-train_index,]
# 3. SVM 모형 적합 -----
svm_model <- svm(Species ~ ., data=train_df, type="C-classification", kernel="linear", scale=FALSE)
# 4. SVM 모형 평가 -----
## 4.1. 훈련데이터
pred_train <- predict(svm_model, train_df)
mean(pred_train == train_df$Species)
[1] 0.9833333
## 4.2. 검증데이터
pred_test <- predict(svm_model, test_df)
mean(pred_test == test_df$Species)
[1] 0.9
# 5. 100회 SVM 모형 평가 ----
accuracy <- rep(NA,100)
for (i in 1:100){
# 1. 훈련/시험 데이터 분할 ----
train_index <- createDataPartition(iris$Species, p = .8, list = FALSE)
train_df <- iris[ train_index,]
test_df <- iris[-train_index,]
# 2. SVM 모형 적합 -----
svm_model <- svm(Species ~ ., data=train_df, type="C-classification", kernel="linear", scale=FALSE)
# 3. SVM 모형 평가 -----
pred_test <- predict(svm_model, test_df)
accuracy[i] <- mean(pred_test == test_df$Species)
}
mean(accuracy)
[1] 0.9833333
sd(accuracy)
[1] 0.02145127