앞서 심층 신경망 예제는 모두 가격 같은 연속 변수를 다루고 있습니다. 하지만 세상에는 몇 가지로 결과가 나눠지는 데이터도 흔합니다. 대표적인 것이 질병 발생입니다. 예를 들어 흡연 같은 위험인자와 폐암 발생과의 상관 관계를 보는 것이죠. 이 경우 원인이 되는 흡연력은 연속변수로 나타낼 수 있지만, 결과 변수인 폐암은 폐암이 생긴 경우와 아닌 경우 두 가지로 밖에 표시할 수 없습니다. 물론 원인이 되는 변수들 역시 몇 가지로 분류되는 변수일 수 있습니다. 예를 들어 남자와 여자, 인종 (아시아계, 백인, 흑인 등) 같은 경우가 그렇습니다.
여기서는 역시 유명한 공개 데이터 가운데 하나인 피마 인디언 데이터를 이용해서 당뇨 발생 여부를 예측하는 신경망 모델을 만들어 보겠습니다. 이 데이터는 피마 인디언 여성에서 당뇨 발생의 여러 위험인자와 발생 여부를 담은 9개의 변수로 되어 있습니다. 데이터는 mlbench 패키지에서 가져올 수 있습니다.
pregnant Number of times pregnant
glucose Plasma glucose concentration (glucose tolerance test)
pressure Diastolic blood pressure (mm Hg)
triceps Triceps skin fold thickness (mm)
insulin 2-Hour serum insulin (mu U/ml)
mass Body mass index (weight in kg/(height in m)\^2)
pedigree Diabetes pedigree function
age Age (years)
diabetes Class variable (test for diabetes)
768개의 관측치와 9개의 변수로 된 데이터인데, 사실 정확한 분석을 위해서는 데이터에 대한 자세한 파악이 먼저지만, 시간 관계상 여기서는 생략합니다. 하지만 위험 인자 8개 가운데 결측치와 범주형 데이터에 대해서는 이해해야 합니다. 임신 횟수, 혈당, 이완기 혈압, 삼두근의 피부 두께, 2시간 혈당 인슐린, 체질량 지수, 당뇨의 가계도, 연령, 당뇨 여부로 되어 있습니다. 그런데 데이터를 살펴보면 이상한 부분을 발견하게 됩니다.
library(neuralnet)
library(mlbench)
library(Metrics)
data(PimaIndiansDiabetes)
pima <- pimaindiansdiabetes="" span="">->
head(pima)
summary(pima)
데이터를 열어서 확인하면 상당히 많은 값이 0으로 표기된 것을 알게 됩니다. 사람 피부 두께가 0이거나 혈당이나 혈압이 0이 될수는 없으니 사실 측정하지 못한 값, 즉 결측값입니다. 특히 삼두근과 인슐린 농도에 많은 결측치가 있어 이대로 학습 데이터로 삼기에는 부족한 부분이 많습니다. 이 경우 평균값으로 결측치를 대치하거나 혹은 다른 방법을 사용해서 결측치를 채울 수도 있으나 아예 분석에서 빼는 것도 방법입니다. 여기서는 분석에서 제외하겠습니다. 그리고 나머지 값에 대해선 결측치를 제거하는 방향으로 데이터를 정제하겠습니다.
pima<-pima age="" c="" diabetes="" glucose="" mass="" pedigree="" pregnant="" pressure="" span="">-pima>
pima=subset(pima,glucose>0)
pima=subset(pima,pressure>0)
pima=subset(pima,mass>0)
이제 앞서 예제와 동일하게 뉴럴넷을 이용해서 신경망 학습을 시켜보겠습니다. 총 724개의 관측치 가운데 600개를 학습에 사용하고 124개를 테스트에 할당하겠습니다. 그런데 그냥 scale 함수를 사용할 경우 에러가 나게 됩니다. 왜냐하면, scale 함수는 범주형 데이터인 diabetes에 사용이 불가능하기 때문이죠. 그래서 데이터를 두 개로 쪼갠 후 다시 붙이는 작업을 진행하겠습니다.
set.seed(1234)
data1<-scale c="" pima="" span="">-scale>
data2<-pima c="" span="">-pima>
data=cbind(data1,data2)
data<-data .frame="" data="" span="">-data>
n = nrow(data)
train <- 600="" n="" sample="" span="">->
test <- data="" span="" train="">->
train <- data="" span="" train="">->
f= data2 ~ pregnant+glucose+pressure+mass+pedigree+age
fit<-neuralnet f="" span="">-neuralnet>
data=train,
hidden=c(6,6,6),
algorithm = "rprop+",
err.fct = "sse",
act.fct = "logistic",
threshold = 0.1,
stepmax=1e6,
linear.output = TRUE)
pred<-compute c="" fit="" span="" test="">-compute>
result<-cbind net="" pred="" span="" test="">-cbind>
result
그런데 이렇게 하면 예상치 않은 결과가 나오게 됩니다.
뉴럴넷 패키지의 단점이 바로 이것인데, 범주형 자료에 대한 분류가 어렵다는 것입니다. 이를 해결하기 위해 round ()를 사용해도 되지만, 1.5를 기준으로 나누면 반올림 했을 때 1,2로 떨어지지 않는 값도 해결이 가능합니다.
result$pred<-ifelse net="" pred="" result="">1.5,2,1)-ifelse>
result
그런데 이렇게하면 과연 맞게 분류한 경우가 얼마나 되는지 한 눈에 알기가 어렵습니다. 역시 ifelse 함수를 이용해서 맞게 예측한 경우와 아닌 경우로 분류해 보겠습니다.
result$error<-ifelse data2="=result$pred,1,0)</div" result="">
-ifelse>
table(result$error)
sum(result$error)/124
> table(result$error)
0 1
38 86
> sum(result$error)/124
[1] 0.6935483871
대략 69%의 확률로 옳게 분류했다고 나오네요. 은닉층을 하나 더 넣어서 예측율을 높일 수 있을까요?
> result$error<-ifelse data2="=result$pred,1,0)</span" result="">-ifelse>
> table(result$error)
0 1
48 76
> sum(result$error)/124
[1] 0.6129032258
오히려 예측 정확도가 떨어졌습니다. 이렇게 예측 모델을 평가해가면서 개선할 수 있긴 하지만, 사실 좋은 신경망 모델이라고 하기는 어려울 것입니다. 다행히 R에는 수많은 패키지가 있고 뉴럴넷 이외에도 다양한 신경망 구축을 돕는 패키지가 존재합니다. 다음에는 이를 알아보겠습니다.
댓글
댓글 쓰기