레미제라블 3D 인물 네트워크

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 3D 시각화 패키지 설치
install.packages("networkD3")
install.packages("dplyr")
 
library(networkD3)
library(dplyr)
 
# data set 소설 레미제라블 인물 관계도
# 데이터 로드
data(MisLinks, MisNodes)
head(MisNodes) # size : 몇번 언급되었는지에 대한 정보
head(MisLinks) # value : 인물끼리 몇번 만났는지 데이터 
 
# plot
D3_network_LM<-forceNetwork(Links = MisLinks, Nodes = MisNodes, 
                            Source = 'source', Target = 'target'
                            NodeID = 'name', Group = 'group',opacityNoHover = TRUE,
                            zoom = TRUE, bounded = TRUE,
                            fontSize = 15,
                            linkDistance = 75,
                            opacity = 0.9)
 
D3_network_LM
cs

 

 

허거덩 너무 신기해~!

 

 

모바일관련 소송 시각화

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# IT 회사들간 법적 소송관계도 시각화
# 데이터 로드
links = '[
  {"source": "Microsoft", "target": "Amazon", "type": "licensing"},
{"source": "Microsoft", "target": "HTC", "type": "licensing"},
{"source": "Samsung", "target": "Apple", "type": "suit"},
{"source": "Motorola", "target": "Apple", "type": "suit"},
{"source": "Nokia", "target": "Apple", "type": "resolved"},
{"source": "HTC", "target": "Apple", "type": "suit"},
{"source": "Kodak", "target": "Apple", "type": "suit"},
{"source": "Microsoft", "target": "Barnes & Noble", "type": "suit"},
{"source": "Microsoft", "target": "Foxconn", "type": "suit"},
{"source": "Oracle", "target": "Google", "type": "suit"},
{"source": "Apple", "target": "HTC", "type": "suit"},
{"source": "Microsoft", "target": "Inventec", "type": "suit"},
{"source": "Samsung", "target": "Kodak", "type": "resolved"},
{"source": "LG", "target": "Kodak", "type": "resolved"},
{"source": "RIM", "target": "Kodak", "type": "suit"},
{"source": "Sony", "target": "LG", "type": "suit"},
{"source": "Kodak", "target": "LG", "type": "resolved"},
{"source": "Apple", "target": "Nokia", "type": "resolved"},
{"source": "Qualcomm", "target": "Nokia", "type": "resolved"},
{"source": "Apple", "target": "Motorola", "type": "suit"},
{"source": "Microsoft", "target": "Motorola", "type": "suit"},
{"source": "Motorola", "target": "Microsoft", "type": "suit"},
{"source": "Huawei", "target": "ZTE", "type": "suit"},
{"source": "Ericsson", "target": "ZTE", "type": "suit"},
{"source": "Kodak", "target": "Samsung", "type": "resolved"},
{"source": "Apple", "target": "Samsung", "type": "suit"},
{"source": "Kodak", "target": "RIM", "type": "suit"},
{"source": "Nokia", "target": "Qualcomm", "type": "suit"}
]'
 
link_df = jsonlite::fromJSON(links)
# node의 index 숫자는 0부터 시작
# dplyr::row_number()가 1부터 숫자를 매기기 때문에 1씩 뺌
node_df = data.frame(node = unique(c(link_df$source, link_df$target))) %>
  mutate(idx = row_number()-1)
 
# node_df에서 index값을 가져와서 source와 target에 해당하는 index 값을 저장
link_df = link_df %>
  left_join(node_df %>% rename(source_idx = idx), by=c('source' = 'node')) %>
  left_join(node_df %>% rename(target_idx = idx), by=c('target' = 'node'))
 
# 데이터 확인
node_df
link_df
 
# 3D 시각화 패키지 설치
library(networkD3)
library(dplyr)
 
# plot
D3_network_LM<-forceNetwork(Links = link_df, 
                            Nodes = node_df, 
                            Source = 'source_idx', Target = 'target_idx'
                            NodeID = 'node', Group = 'idx',
                            opacityNoHover = TRUE, zoom = TRUE
                            bounded = TRUE,
                            fontSize = 15,
                            linkDistance = 75,
                            opacity = 0.9)
 
D3_network_LM
 
cs

 

Microsoft 깡패...

 

 

 

 

 

 

paper1.csv
0.00MB
book_hour.csv
0.00MB

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 친구를 만난 횟수 데이터 불러오기
paper <- read.csv("c:/data/paper1.csv" , header = T)
 
# 결측치 매꾸기
paper[is.na(paper)] <- 0
 
# rowname을 이름으로 바꾸기
rownames(paper) <- paper[,1]
 
# x 테이블 지우기
paper <- paper[-1]
 
# 확인 : dataframe을 matrix 형식으로 바꾸기 위한 작업들이었음
paper
 
# dataframe -> matrix
paper2 <- as.matrix(paper)
paper2
 
# 책 읽은 시간에 대한 데이터 불러오기
book <- read.csv("c:/data/book_hour.csv" , header = T)
book
 
# 시각화를 위한 라이브러리
library(sna)
 
# 빈 창 띄우기
x11()
 
# 시각화
gplot(paper2 , displaylabels = T, boxed.labels = F , vertex.cex = sqrt(book[,2]) , vertex.col = "blue" , vertex.sides = 20 ,
      edge.lwd = paper2*2 , edge.col = "green" , label.pos = 3)
 
cs

 

 

파란색 공 : 책을 읽은 정도 / 초록색 : 자주 만난 정도

 

 

building.csv
0.00MB

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 데이터 로드 
build <- read.csv("c:/data/building.csv" , header = T)
 
# 결측치를 0으로 채움
build[is.na(build)] <- 0  
head(build)
 
# x 컬럼 삭제
build <- build[-1]
 
# arules 패키지 설치
# install.packages("arules")
library(arules) 
 
# 연관규칙 모델 생성
trans <- as.matrix(build , "Transaction")
rules1 <- apriori(trans , parameter = list(supp=0.2 , conf = 0.6 , target = "rules"))
rules1 
 
# 연관규칙 확인
inspect(sort(rules1))
 
# 신뢰도 0.7이상인 것만 추출
rules2 <- subset(rules1 , subset = lhs %pin% '보습학원' & confidence > 0.7)
inspect(sort(rules2)) 
 
rules3 <- subset(rules1 , subset = rhs %pin% '편의점' & confidence > 0.7)
inspect(sort(rules3)) 
 
# 시각화
# install.packages("sna")
# install.packages("rgl")
library(sna)
library(rgl)
 
# 희소행렬로 만듦
b2 <- t(as.matrix(build)) %*% as.matrix(build)
# 대각성분을 0으로 만듦
b2.w <- b2 - diag(diag(b2))
 
#rownames(b2.w) 
#colnames(b2.w) 
 
gplot(b2.w , displaylabel=T , vertex.cex=sqrt(diag(b2)) , vertex.col = "green" , edge.col="blue" , boxed.labels=F , arrowhead.cex = .3 , label.pos = 3 , edge.lwd = b2.w*2
 
cs

 

 

학원은 카페, 은행과 연관되어 있음을 확인

 

 

 

 

Apriori 알고리즘이란?

Apriori 알고리즘이란 연관규칙(association rule)으로, 특정 사건이 발생하였을 때 함께 (빈번하게) 발생하는 또 다른 사건의 규칙을 말한다.

 

 

Apriori 알고리즘을 사용하는 대표적인 예

1. 암 분석 시 빈번히 발생하는 DNA 패턴과 단백질 서열 검사

2. 신용카드 사기를 당했을 때 주로 결제되는 내역 패턴

3. B2C에서 구매자의 구매 상품과 비슷한 상품 추천

 

 

연관규칙에 사용하는 3가지 통계 척도

종류 설명 표기식
지지도(support) 특정 아이템이 데이터에서 발생하는 빈도 s(X) = n(X) / N

n(X) : 아이템 X의 거래건수
N : 전체거래건수
s(X,Y) = n(X∩Y) / N

n(X∩Y) : 아이템 X와 Y를 포함하는 거래건수
N : 전체거래건수
신뢰도(confidence) 두 아이템의 연관규칙이 유용한 규칙일 가능성의 척도 c(X→Y) = n(X∩Y)/n(X)

n(X∩Y) : 아이템 X와 Y를 포함하는 거래건수
n(X) : 아이템 X의 거래건수

아이템 X를 포함하는 거래 중에서 아이템 Y도 포함하는 거래비율을 말한다.
신뢰도가 높을수록 유용한 규칙일 가능성이 높다.
향상도(lift) 두 아이템의 연관규칙이 우연인지 아닌지를 나타내는 척도 lift(X→Y) = c(X→Y) / s(Y)
아이템 X가 주어지지 않았을 때의 아이템 Y 확률 대비 아이템 X가 주어졌을 때의 아이템 Y 확률 비율을 나타낸다.
향상도가 1보다 크거나(+관계) 작으면(-관계) 우연적 기회보다 우수함을 의미한다.

연산식이 교집합(∩)인지, 합집합(∪)인지에 대해서 헷갈릴 수 있겠는데

그냥 조건부 확률을 구한다고 생각하자.

 

아래의 문제를 통해 연관규칙 알고리즘을 이해해보자.

 

거래번호 거래 아이템
1 , 버터,
2 ,
3 우유, 빵
4 버터, 맥주, 오징어

 

문제 1. s(우유,시리얼) = n(X∩Y) / N = 2/4

 

          우유와 시리얼이 같이 있는 경우를 교집합(∩)으로 써야할까, 합집합(∪)으로 써야할까?

          나같은 경우는 수학적으로 생각했을 때, 교집합이 맞다고 생각해서 교집합을 썼지만 언어적으로는 합집합이

          맞는것 같다. 따라서 이것은 맥락적으로 이해하는 것이 매우 중요하다.

 

문제 2. c(우유→시리얼) = n(X∩Y) / n(X) = 2/3

 

문제 3. lift(우유→시리얼) = c(우유→시리얼) / s(우유,시리얼) = (2/3) / (2/4) = 1.333

 

 

 

 

 

 

 


 

 

 

 

 

PRUNING (가지치기)

모든 아이템들간의 관계성을 알아봐야하기 때문에, 데이터가 많아지면 지수적으로 경우의 수가 많아져 계산량이 많아진다.

따라서 우리는 이것을 효율적으로 수행해야한다.

 

거래번호 아이템 목록
1 A,B,D
2 B,C,
3 A,B,C,E
4 B,C,E

 

1. 각각의 아이템에 대한 지지도를 구한다.

(원래 지지도(s)는 구매건수 / 전체구매건수 이지만 가지치기에서는 단순히 아이템의 갯수로 처리한다.)

 

아이템 지지도
A 2
B 4
C 3
D 1
E 2

 

2. 위의 결과에서 지지도가 1보다 큰 것을 추출한다.

 

아이템 지지도
A 2
B 4
C 3
E 2

 

1-2. 아이템을 2개 조합으로 만들어 지지도를 구한다.

 

아이템 지지도
A B 2
A C 1
A E 1
B C 2
B E 1
C E 2

 

2. 위의 결과에서 지지도가 1보다 큰 것을 추출한다.

 

아이템 지지도
A B 2
B C 2
C E 2

 

1-3. 아이템을 3개 조합으로 만들어 지지도를 구한다.

 

아이템 지지도
A B C 1
A B E 1
A C E 1
B C E 2

 

2. 위의 결과에서 지지도가 1보다 큰 것을 추출한다.

 

아이템 지지도
B C E 2

 

가지치기(Pruning)를 당한 것들에 대해서는 지지도를 계산하지 않는다.

 

 

 

 

 

이외에도 효율적으로 연관성 분석을 하는 방법이 있으니, 블로그를 참고하길 바란다.

 

 

 

 

 

 

wine.csv
0.01MB

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 분류를 위한 nnet 신경망 패키지 설치
install.packages("nnet")
library(nnet)
 
# 이원교차표를 보기 위함
library(gmodels)
 
wine <- read.csv("c:/data/wine.csv",header=T,stringsAsFactors = T)
 
# 확인
head(wine)
str(wine)
 
# scale 함수를 사용한 정규화 (평균 0, 표존편차 1)
wine.scale <- cbind(wine[1], scale(wine[-1]))
summary(wine.scale)
 
size <- nrow(wine.scale) # 178
 
# shuffle
set.seed(100)
index <- c(sample(1:size, size * 0.7))
 
# train(70%) / test(30%)
train <- wine.scale[index, ]
test <- wine.scale[-index, ]
 
# 분류를 위한 신경망 모델 생성
model.nnet2 <- nnet(Type~., data = train, size = 2, decay = 5e-04, maxit = 200)
#                                         number of units in the hidden layer.
#                                                   parameter for weight decay. Default 0. (가중치 감소)
#                                                                  maximum number of iterations. Default 100 (반복)
 
# 분류결과 확인
predicted <- predict(model.nnet2, test, type = "class")
predicted
 
# 모델 평가를 위한 Confusion Matrix
# 참고 : https://yamalab.tistory.com/50
actual <- test$Type
model.confusion.matrix <- table(actual, predicted)
 
# 모델 평가
<- CrossTable(model.confusion.matrix)
x$prop.tbl[1]+x$prop.tbl[5]+x$prop.tbl[9# 0.9444444
 
cs

 

 

 

 

concrete.csv
0.04MB

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 신경망을 이용하여 콘크리트 강도 예측하기
concrete <- read.csv('c:/data/concrete.csv')
 
# 예측하고자 하는 결과가 정규분포를 띄는지 확인한다.
# 만약 정규분포를 띈다면 예측 결과가 좋게 나올 수 있다는 것을 알 수 있다.
hist(concrete$strength)
 
# 결측치 확인
colSums(is.na(concrete))
 
# 정규화 함수
normalize <- function(x) {
  return ( (x-min(x)) / (max(x) - min(x) ) )
}
 
# 데이터 정규화하기
concrete_norm <- as.data.frame(lapply(concrete,normalize))
 
# 0~1 사이로 데이터가 바꼈는지 확인
summary(concrete_norm$strength)
# 기존 데이터와 비교
summary(concrete$strength)
 
# train(75%) / test(27%)
nrow(concrete_norm) * 0.75 # 772.5
concrete_train <- concrete_norm[1:773, ]
concrete_test <- concrete_norm[774:1030, ]
 
# 신경망 패키지 설치
install.packages("neuralnet")
library(neuralnet)
 
# 모델 생성
concrete_model <- neuralnet(formula=strength ~ cement + slag + ash +water +superplastic + coarseagg + fineagg + age,
                            data =concrete_train)
 
# 신경망 모델 시각화
plot(concrete_model)
 
# 예측
model_results <- compute(concrete_model, concrete_test[1:8])
predicted_strength <- model_results$net.result
 
# 상관관계 확인
cor(predicted_strength, concrete_test$strength) # 0.806285848
cs

 

 

시각화

 

 

 

 

 

 

모델 성능 개선

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 모델 성능 개선 은닉층(hidden) 생성
concrete_model2 <- neuralnet(formula=strength ~ cement + slag + ash +
                               water +superplastic + coarseagg + fineagg + age,
                             data =concrete_train , hidden=c(5,2))
 
# 은닉층을 설정해 생성한 신경망 모델 시각화
plot(concrete_model2)
 
# 예측
model_results <- compute(concrete_model2, concrete_test[1:8])
predicted_strength2 <- model_results$net.result
 
# 상관관계 확인
cor(predicted_strength2, concrete_test$strength)
cs

 

hidden=c(5,2) 첫번째 은닉층 5개 / 두번째 은닉층 2개

 

 

 

 

 

다층퍼셉트론

다층 퍼셉트론

 

위의 이미지를 보자. 앞에서 설명했던 기본적인 퍼셉트론이 여러개의 층으로 이루어지는 것을 다층 퍼셉트론이라고 한다. Input은 입력층, Hidden은 은닉층, Output은 출력층으로 표현한다. 여기서 은닉층(Hidden)이 여러개를 갖게 되는 것을 다층 퍼셉트론이라고 할 수 있다.

 

 

 

model = Sequential()
model.add(Dense(12, input_dim=8, activation='relu'))
model.add(Dense(8, activation='relu'))
model.add(Dense(1, activation='sigmoid'))

 

위의 코드는 다층퍼셉트론을 만드는 기본적인 코드이다. 그런데 저 코드에서 activation에 있는 relu와 sigmoid는 도대체 뭘까? 우리는 이것들을 활성화 함수라고 한다.

 

 

 

 

 

활성화 함수

 

위의 그림은 신경망을 나타내는 그림이다. 여기서 활성화 함수에 해당하는 것은 계단함수이다. 계단함수라는 것은 특정 임계값을 넘기면 활성화되는, 즉 0과 1로 출력되는 함수를 의미한다.

 

더보기
import matplotlib.pylab as plt
import numpy as np

def step_function(x) :
    result = x > 0
    return result.astype(np.int)

x = np.arange(-5,5,0.1)
y = step_function(x)
plt.plot(x,y)
plt.ylim(-0.1,1.1)
plt.show()

파이썬으로 구현한 계단함수

 

하지만 단층퍼셉트론이 아니라 다층퍼셉트론 같은 경우는 활성화 함수로 계단함수를 쓸 수 있을까? 어떤 기준으로 0과 1을 나눠야 하는걸까? 그것에 대한 해답이 sigmoid와 relu이다.

 

 

 

활성화 함수

 

 

 

 

 


 

 

 

 

 

  Sigmoid

더보기
import matplotlib.pylab as plt
import numpy as np

def sigmoid(x) :
    return 1/(1+np.exp(-x))

x = np.array([-1,1,2])
sigmoid(x)

x = np.arange(-5,5,0.1)
y = sigmoid(x)
plt.plot(x,y)
plt.ylim(-0.1,1.1)
plt.show()

파이썬으로 구현한 Sigmoid함수

 

위의 그래프에 대해서는 내가 이전에 맥락을 풀어 설명했으니 참고하길 바란다. 하지만 귀찮은 사람들을 위해 간략하게 말하자면 그래프 그대로 데이터를 0과 1로 분류한다는 뜻이다. 이것이 Sigmoid 함수이다. 앞서 말했지만 퍼셉트론은 인간의 뉴런을 모델로 했기 때문에 다층 퍼셉트론같은 경우 데이터를 0과 1로 구분하여 역치 이상(1)일 경우 데이터를 다음 퍼셉트론으로 넘기는 방식으로 운영되어진다. 그런데 이쯤 되면 궁금증이 생긴다. 계단함수나 Sigmoid나 둘 다 0 또는 1 혹은 0~1 사이의 값으로 데이터를 변환해준다는 것이다. 그런데 왜 단층퍼셉트론에는 계단함수를 쓰고 다층퍼셉트론에서는 Sigmoid 또는 ReLU를 사용하는 것일까?

 

다층 퍼셉트론은 여러개의 은닉층을 보유하고 앞에서 가공되어진 데이터들이 다음 은닉층으로 넘어가는 구조이다. 계단함수를 다층 퍼셉트론에서 사용한다면 우리는 이전 데이터를 오직 0과 1, 즉 극단적인 형태로밖에 받을 수 없고 이런식이라면 다층 퍼셉트론을 만들어 좋은 모델을 만들 수 없게 된다. 하지만 Sigmoid 함수같은 경우는 값을 0~1 사이로 만들어주기 때문에 가공된 데이터를 다음 은닉층으로 보내줘도 극단적인 형태가 아니고 신경망이 데이터를 섬세하게 분류할 수 있도록 도와준다.

 

좋다. 계단함수보다 Sigmoid를 사용하는 이유를 이제는 충분히 이해했다. 그런데 ReLU는 또 왜 필요한걸까?

 

 

ReLU

더보기
import matplotlib.pylab as plt
import numpy as np

def relu(x) :
    return np.maximum(0,x)

x = np.arange(-5,5,0.1)
y = relu(x)

plt.plot(x,y)
plt.ylim(-0.5,5.5,0.1)
plt.show()

파이썬으로 구현한 ReLU함수

 

ReLU함수는 Sigmoid 단점을 해결하기 위해 나왔다. Sigmoid 함수의 단점은 0과 1사이의 데이터로 이루어져 있어 역전파를 할수록, 즉 층이 깊어질수록 활성화 값들이 0과 1에 치우쳐져 있어 미분값이 0에 가까워진다는 것이다. 이것을 Vanishing Gradient Problem(기울기 값이 사라지는 문제)이라고 한다. 즉, 전파가 역전파될 기울기 소실로 인해서 앞층까지 전파가 안된다는 이야기다. 따라서 이와 같은 문제점을 해결하고자 ReLU 함수를 사용하게 된 것이다.

 

ReLU는 0보다 작은 값에 대해서는 0을 반환하고, 0보다 큰 값에 대해서는 그 값을 그대로 반환하여 층이 깊어져도 0으로 수렴하는 오류를 범하지 않게 된다.

 

 

더 자세한 내용은 블로그를 참고하길 바란다.

 

 

+

 

 

Leaky ReLU

더보기
def leaky_relu(x) :
    return np.maximum(0.01*x,x)

x = np.arange(-20,5,0.1)
y = leaky_relu(x)

plt.plot(x,y)
plt.grid()
plt.ylim(-0.5,5.5,0.1)
plt.show()

 

파이썬으로 구현한 Leaky ReLU

 

ReLU와 동일하나 x가 음수일때 기울기(gradient)가 0.01이다.

 

 

ELU (Exponential Linear Units)

ELU 식

ELU 또한 ReLU와 비슷하나 기울기가 살아남는다는 특징이 있다.

 

 

 

'인공지능 > 인공지능 이론' 카테고리의 다른 글

16. 배치(Batch), 미니배치 학습, 에폭(Epoch), SGD  (3) 2020.08.04
15. 항등함수와 Softmax 함수  (2) 2020.08.03
13. Perceptron (퍼셉트론)  (0) 2019.09.14
12. Clustering  (0) 2019.09.13
11. 차원의 저주 / PCA  (0) 2019.09.13

 

 

 

test_vif1.csv
0.00MB
test_vif2.csv
0.00MB

 

 

 

※ 전공자가 아니기 때문에 비약적인 부분이 많을 수도 있겠으나,

대략적인 흐름을 설명하고자 하는 노력이니 전공자분들이 hoxy나 이 글을 보고 오류를 찾는다면 알려주시길 바람.

 

 

회귀분석에서 검정이 필요한 이유

우리가 회귀분석을 하는 이유가 애초에 무엇인지 생각해보자. 우리는 일정한 패턴을 갖는 데이터들로 하여금 (선형적인) 식을 만들어 그와 비슷한 데이터를 새롭게 입력받은 경우에도 문제를 해결, 혹은 예측하고자 회귀분석을 한다. 위의 데이터들을 이용하여 다음과 같은 함수를 R에서 구현해보자.

 

 

test_vif1

1
2
3
4
5
6
7
8
9
10
11
12
13
library(car) # vif 함수용
test <- read.csv('c:/data/test_vif1.csv')
test
 
# test에 있는 학생정보는 불필요하므로 제외
test <- test[-1]
 
# 회귀 모델 생성
regression_model <- lm(test$시험점수~.,data=test)
summary(regression_model)
 
# 팽창계수 확인
vif(regression_model)
cs

 

회귀 모델 요약

 

test_vif1은 시험점수, 아이큐, 공부시간에 대한 컬럼이 존재하는 데이터로 다중회귀분석에 해당하는 데이터이다. 종속변수(Y), 즉 (예측)결과인 시험점수는 독립변수(X)인 아이큐와 공부시간에 의해 나타낼 수 있다. 내가 방금 한 말을 요약하자면 아이큐와 공부시간에 의해 시험점수를 나타낼 수 있다는 것이다. 그런데 왜 굳이 회귀식을 만들어 놓고선 summary를 하고 그것을 굳이굳이 캡쳐까지 해서 올려놓은것일까? 그것에 대해 이제부터 설명하도록 하겠다. 우선 위의 그림에서 Coefficients(계수) 테이블 중 t value와 Pr(>|t|) 를 보자.

 

 

우리가 t-value와 p-value(Pr(>|t|)의 값을 보는 이유는, 이 독립변수들이 정말 회귀식을 구성하는데 필요한지에 대해 보기 위해서이다. 그리고 t-value와 p-value를 이해하기 위해서는 귀무가설과 대립가설에 대해 이해할 필요가 있다. 더 디테일한 내용에 대해서는 구글링(크흡 비전공자라 미안해...)을 하길 바란다.

 

 

 

 

 


 

 

 

 

다중 회귀식

 

 

t value : 잔차를 표준편차로 나눈값

t value의 귀무가설은 α_i(회귀계수) = 0 이다. 즉, 귀무가설을 만족한다는 것은 회귀계수 = 0 을 만족하는 것이고 이 말은 즉슨, 주어진 데이터인 X가 종속변수(Y)에 영향을 미치지 않는다는 것을 의미한다. 결국에는 쓸모 없는 데이터란 말이다. t value를 구하는 방법은 구글링을 통해 찾아보는 것을 추천한다.

 

 

Pr(>|t|) : p value

Pr(>|t|)는 p value를 의미하고 p value는 위에 구한 t value값을 기준에 해당하는 값(|t|)과 비교하여 나타낸 값으로, 이 값은 유의수준에 적합하면 할수록, 즉 0.05보다 작고 0에 가까울수록 매우 유의미한 데이터라고 인식하고 0.05보다 크면 불필요한 데이터에 가깝다고 인식한다. 위의 summary를 보면 p value 값 옆에 * 표시들이 있는데 *이 많을수록 유의미한 데이터임을 나타낸다.

 

 

 

이렇게 t value와 p value를 통해 1차적으로 독립변수가 회귀식에 필요한지 아닌지를 우선적으로 판단한다.

test_vif1 데이터에서 아이큐와 공부시간은 귀무가설을 기각하기 때문에 회귀식에 필요한 독립변수임을 알 수 있다.

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

test_vif2

1
2
3
4
5
6
7
8
9
10
11
12
13
library(car) # vif 함수용
test <- read.csv('c:/data/test_vif2.csv')
test
 
# test에 있는 학생정보는 불필요하므로 제외
test <- test[-1]
 
# 회귀 모델 생성
regression_model <- lm(test$시험점수~.,data=test)
summary(regression_model)
 
# 팽창계수 확인
vif(regression_model)
cs

 

회귀 모델 요약

 

위의 test_vif1와 같은 방식으로 회귀 모델을 요약해보았다. test_vif2의 컬럼에는 등급평균이 추가되었다. 위의 summary를 보면 아이큐와 등급평균의 p value 값이 유의수준 0.05보다 크다는 것을 알 수 있다. 이것은 결론적으로 회귀식에서 아이큐와 등급평균이 유의미하지 않다는 것을 의미한다. 어떻게 이런 일이 생긴걸까? 등급평균 하나만 추가한건데 말이다!

 

이러한 경우, 아이큐와 등급평균 두 개의 데이터는 불필요해진다. 하지만 정말 과연 그럴까? 어떻게 아이큐와 공부시간만 있을 때는 2개의 독립변수로 나타내지는 다중회귀식이 만들어지는데 지금과 같은 경우는 오직 공부시간에만 영향을 받는 단순선형회귀식이 되어버린걸까? 또한 단순선형회귀식이 된다면 이전의 회귀모델보다 데이터의 정확도가 떨어지게 될 것이다. (아니 어떻게 모은 데이터인데 안 모으니만 못하다니 이게 뭔소리야!!!!)

 

이러한 경우를 위해 우리는 팽창계수를 확인한다.

 

 

 

팽창계수(vif)

 

팽창계수 확인

 

위의 팽창계수를 보자. 아이큐와 등급평균이 22, 19로 매우 높은 값을 나타낸다는 것을 알 수 있다. 이렇게 팽창계수가 높다는 것은 다중공선성이 있다는 뜻이고, 다중공선성이 있다함은 독립변수들 중에 서로 영향을 주는 것이 있다는 뜻이다. 서로 영향을 주는데 그게 어떻게 독립변수겠는가? 따라서 우리는 서로 영향을 주는 변수들 중 하나만을 남겨 독립변수로서의 성능을 할 수 있도록 해줘야한다. 팽창계수를 확인하여 독립변수인지 아닌지를 판단하는 방법에 대해서, 현업 기준으로는 팽창계수가 보통 10보다 큰 것을 골라내고, 엄격하게는 5보다 큰 것, 느슨하게는 15~20보다 큰 것을 골라낸다.

 

위의 데이터에서는 아이큐와 등급평균이 기준 이상이므로 둘 중 하나만을 지워야 한다. 어떤 변수를 지워야할까?

결론은 팽창계수가 큰 것부터 없애야한다 이다. 현재 test_vif2의 데이터 같은 경우는 컬럼이 고작 3개 뿐이기 때문에 팽창계수가 큰 아이큐와 등급평균이 서로 영향을 준다는 것을 알 수 있다. 하지만 만약 컬럼이 30개라면? 그 이상이라면? 팽창계수가 큰 것이 10개 이상이라면? 우리는 어떤 컬럼들이 서로에게 영향을 주는지 알 수 없다. 그렇기 때문에 우선적으로 팽창계수가 가장 큰 컬럼부터 지워나가며 다시 회귀모델을 만들고, 또 거기서 팽창계수가 큰 것을 지워나가는 것을 반복하며 회귀모델을 만들어가야 한다.

 

+) 전공생분과의 대화를 통해서 알게 된 사실은 팽창계수 뿐만 아니라 상관관계로도 판단할 수 있다! 다만 상관관계로 판단하기에는 컬럼이 너무 많아서 눈에 쉽게 안 들어오니까 쉬운게 좋은 나는 대체로 팽창계수가 큰 것부터 없애는 방향으로 하려고 했는데, 노력을 조금 더 해서 정확도를 높이고자 하면 상관관계가 있는 변수들을 파악하여 그 중에서 종속변수와 연관성이 더 높은 변수를 살리는 것이 좋다. 이것은 개인의 판단이자 몫이니 참고하길 바란다!

 

 

 

 

 

 


 

 

 

 

 

결론

정확도가 높은 회귀 모델을 만들기 위해서는

1. p value로 독립변수(컬럼)가 회귀식에 필요한지 아닌지를 우선적으로 판단

2. 만약 p value에서 귀무가설을 기각하지 않는다면 팽창계수(vif)를 확인하여 다중공선성이 있는 독립변수(컬럼)를 하나씩 지워나감

     OR 상관계수로 파악하여 서로 영향을 주는 변수를 파악하고, Y(종속변수)와 상관관계가 비교적 작은 독립변수를 하나씩 지워나감

 

 

 

 

 

 

+ Recent posts