비선형 차원축소 :: 통계 - mindscale
Skip to content

비선형 차원축소

  • s자 패턴이 보임
  • 곡선의 패턴을 새로운 축으로 잡음
  • 축을 직선으로 펼침
  • 2차원 공간에 있던 데이터가 1차원에서 존재

비선형 차원축소의 방법들

  • Autoencoders
  • 뉴럴네트워크나 딥러닝에서 많이 볼 수 있음
  • Kernel principal component analysis
  • 커널 트릭을 넣어 선형 방법을 비선형 방법으로 바꿈
  • IsoMap
  • Locally-linear embedding(LLE)
  • Modified Locally-Linear Embedding(MLLE)
  • LLE를 확장한 개념
  • t-distributed stochastic neighbor embedding

  • 이번 강의에서는 Isomap, Locally-linear embedding, t-distributed stochastic neighbor embedding 세 가지 방법을 다룰 예정

IsoMap

  • MDS와 비슷
  • MDS는 모든 데이터들의 거리를 거리 행렬로 만들어 그 행렬에 맞게 점들을 위치시킴
  • IsoMap은 이웃에 있는 점들과의 거리만을 사용함
  • 멀리 떨어진 점들과의 거리는 이웃한 점들을 이어서 사용함
  • 그 이유는 공간이 휘어져 있을 때 상대적으로 가까이 있지만 끊어진 부분이 있을 수 있는데 IsoMap은 끊어진 부분들을 무시하고 이어진 부분을 연속으로 따라가서 거리를 재기 때문
  • 스위스 롤케익 문제
  • 이러한 데이터를 MDS나 PCA를 하게 되면 평평하게 되거나 모양이 말려 있는 쪽의 면을 보여줌
  • 그러나 IsoMap을 이용하면 말려 있는 것을 펼칠 수 있음

LLE

  • IsoMap과 비슷함
  • PCA와 비슷함
  • 원래 차원에서 한 점과 이웃한 점들의 관계를 찾음
  • 낮은 차원에서 같은 관계가 유지되도록 점의 좌표를 찾음
  • 휘어진 데이터라고 해도 부분만 보면 직선처럼 보임
  • 각 색만 보면 평평함
  • 따라서 각 색별로 이어 평평하게 만들 수 있음

t-SNE

  • IsoMap과 LLE는 연속적인 형태에 유리하기 때문에 끊어져 있는 데이터나 흩어져 있는 데이터와는 잘 맞지 않음
  • t-SNE는 데이터의 부분적 구조에 초점, 복잡한 형태에 유리
  • 큰 차원의 데이터를 낮은 차원으로 내리기 때문에 전체적인 구조를 잘 보존하지 못하고 부분적인 구조를 잘 보존함
  • 돌아가는데 시간이 오래 걸림
  • 돌릴 때마다 결과가 달라짐

비선형 차원 축소 실습

데이터 다운 링크

  • 목적: 말려 있는 데이터를 펼쳐 2차원에 보기 좋게 보여주기

디렉토리 설정 후 데이터 불러오기

roll = read.csv('swissroll.csv')

패키지 설치

install.packages(c('rgl', 'lle', 'tsne', 'vegan'))

3차원 그래프 그리기

library(rgl)
plot3d(roll)

시각화를 위해 점들에 색깔 정하기

cent.dist = sqrt(rowSums(roll[,c(1,3)]^2))
min.dist = min(cent.dist)
max.dist = max(cent.dist)
color = rainbow(12)[round(1 + 11*(cent.dist - min.dist)/(max.dist - min.dist))]
plot3d(roll, col = color)
- 중심으로부터 거리에 따라 색을 다르게 입힘

PCA

p = prcomp(roll)
plot(p$x, col = color)
- 옆에서 자른 것처럼 그림이 그려짐 - 따라서 비선형 차원 축소를 해야 함

MDS

d = dist(roll)
mds = cmdscale(d)
plot(mds, col = color)
- PCA와 비슷하게 나옴

IsoMap

library(vegan)
d = dist(roll)
iso = isomap(d, ndim = 2, k = 30)
plot(iso$points, col = color)
- ndim = 2: 2차원으로 줄일 예정 - k = 30: 이웃할 점들의 개수 30개 - 가까이에 있는 점들을 MDS해서 2차원에 그림 - 3차원 이상이어도 가능함 - 그래프의 세로 및 가로 축의 숫자는 크게 신경 쓰지 않아도 됨

LLE

library(lle)
lin = lle(roll, 2, 30)
plot(lin$Y, col = color)
- LLE도 마찬가지로 함수에 줄일 차원 수와 이웃할 점들의 개수를 같이 적어주면 됨 - 덜 펼쳐지기는 했지만 원래 데이터를 펼쳐서 보여줌

t-SNE

library(tsne)
ts = tsne(roll, initial_dims = 3)
plot(ts, col = color)
- initial_dims = 3: 초기의 차원을 몇 개로 할 것인지 - 계속 반복해서 돌면서 조금씩 결과를 좋게 바꿔나가는 식으로 계산 - 기본 옵션이 계산을 1000번 함 - 100번마다 결과를 알려줌 - 결과를 알려줄 때마다 에러를 보여주는데 그 에러가 점점 줄음 - 부분적인 구조는 보존하지만 전체적인 구조는 보존하지 않아 모양이 특이함 - 중간에 끊어진 부분들이 있음 - 연속적으로 이어진 경우에는 LLE나 IsoMap이 더 좋음

정리

차원 축소를 할 때 시각화를 해보지 않으면 잘 알 수 없기 때문에 아는 방법들을 한 번씩 해보고 제일 좋은 결과를 도출하는 방법을 선택

사례 연구: 이미지 데이터의 시각화

데이터 다운 링크

  • 목적: 손글씨 이미지 파일을 2차원에 시각화하기
  • 8 X 8 사이즈의 이미지 파일, 즉 64차원
  • 밝기: 1~16

패키지 설치

install.packages(c('lle', 'tsne', 'vegan'))

데이터 파일 불러오기

data = read.csv('digit.csv')

데이터 정보

dim(data)
  1. 1797
  2. 65
  • 1797개의 손글씨 이미지
  • 65개의 종: 64개의 점의 밝기 + 손글씨 숫자가 무슨 숫자인지에 대한 정보

첫 번째 손글씨의 정보

data[1,1:8]
V1V2V3V4V5V6V7V8
0 0 5 139 1 0 0
data[1,9:16]
V9V10V11V12V13V14V15V16
0 0 131510155 0
data[1,17:24]
V17V18V19V20V21V22V23V24
0 3 152 0 118 0
data[1,25:32]
V25V26V27V28V29V30V31V32
0 4 120 0 8 8 0

시각화를 위한 사전 작업

image = data[1:64]
number = as.character(data[,65])
color = rainbow(10)[data[,65]+1]
  • image: 이미지에 해당하는 부분
  • number: 숫자 정보에 해당하는 부분
  • number는 후에 텍스트 형태로 출력을 하기 위해 문자 형태로 변환함
  • color: 숫자 별로 색을 지정

PCA

pca = prcomp(image)
plot(pca$x, typ = 'n')
text(pca$x, number, col = color)
  • 64차원의 이미지를 2차원으로 축소
  • 비슷한 숫자들끼리 비슷한 위치에 있음
  • 5, 7, 8이 잘 구분이 안되어 있음
  • 비선형적인 패턴을 많이 가진 부분은 잘 표현되지 못함

MDS

d = dist(image)
mds = cmdscale(d)
plot(mds, typ = 'n')
text(mds, number, col = color)
  • 5, 7, 8이 잘 구분이 안되어 있음

IsoMap

library(vegan)
iso = isomap(d, ndim = 2, k = 30)
plot(iso$points, typ = 'n')
text(iso$points, number, col = color)
Loading required package: permute
Loading required package: lattice
This is vegan 2.5-6
  • 앞에서 했던 것보다 숫자들끼리 덜 겹쳐짐
  • 원래 공간에 복잡하게 접혀져 있는 것들을 잘 펼쳐서 표현
  • IsoMap은 거리만을 이용하니까 이번 사례에서는 LLE보다 잘 나옴

LLE

library(lle)
lin = lle(image, 2, 30)
plot(lin$Y, typ = 'n')
text(lin$Y, number, col = color)
Loading required package: scatterplot3d
Loading required package: MASS
Loading required package: snowfall
Loading required package: snow


finding neighbours
calculating weights
computing coordinates
  • 결과가 좋지 않음
  • LLE는 평면이 있을 것이라고 가정을 하는데 이 데이터에는 평면이 없어 이런 결과를 도출한 듯함

tsne

library(tsne)
ts = tsne(image, initial_dims = 64)
plot(ts, typ = 'n')
text(ts, number, col = color)
sigma summary: Min. : 0.221408575610339 |1st Qu. : 0.620063784622015 |Median : 0.675069430571825 |Mean : 0.687671788667607 |3rd Qu. : 0.739130003054694 |Max. : 1.52805698437237 |
Epoch: Iteration #100 error is: 19.217536943355
Epoch: Iteration #200 error is: 1.3587223315932
Epoch: Iteration #300 error is: 1.14080852254214
Epoch: Iteration #400 error is: 1.04440997888217
Epoch: Iteration #500 error is: 1.00637787550838
Epoch: Iteration #600 error is: 0.986854976155903
Epoch: Iteration #700 error is: 0.975567595309222
Epoch: Iteration #800 error is: 0.968378888065802
Epoch: Iteration #900 error is: 0.963365469441065
Epoch: Iteration #1000 error is: 0.959970056385717
  • 숫자별로 구분이 잘 되었음
  • 겹치는 숫자도 거의 없음
  • 부분적인 구조를 잘 보존했기 때문에 이런 결과가 나옴
  • 전체적인 구조를 잘 보존하지 못한다는 단점이 이 사례에서는 장점이 되었음