데이터의 '실제(유용한)' 차원은 주어진 데이터의 차원과 다를 수 있습니다.
그리고 차원의 저주에 걸릴 수도 있죠
https://en.wikipedia.org/wiki/Curse_of_dimensionality
Curse of dimensionality - Wikipedia
From Wikipedia, the free encyclopedia Jump to navigation Jump to search The curse of dimensionality refers to various phenomena that arise when analyzing and organizing data in high-dimensional spaces (often with hundreds or thousands of dimensions) that d
en.wikipedia.org
2차원으로 예를 들긴 좀 그렇지만 데이터의 편차가 x축이나 y축이 아닌 다른 차원을 따라 발생한 것입니다.
(TIP.차원 축소는 고차원 데이터에서 편차를 가장 잘 잡아낼 수 있는 부분 집합을 찾아낼 때 유용합니다.)
지금부터 주성분 분석(Principal Component Analysis)를 통해 데이터의 편차를 최대한 포착하는 차원을 찾아 보겠습니다.
가장 먼저 각 차원의 평균이 0이 되도록 데이터를 바꿔줍니다.
def de_mean_matrix(A): """A의 모든 값에서 각 열의 평균을 빼준 행렬을 반환 반환된 행렬의 모든 열의 평균은 0""" nr, nc = shape(A) column_means, _ = scale(A) return make_matrix(nr, nc, lambda i, j: A[i][j] - column_means[j])
위 코드에서 평균이 왜 0이 되는지 모르는 분은 편차의 합이 0이고 따라서 편차의 평균 역시 0이 된다는 것을 기억하세요
각 열의 평균을 0으로 하는 이유는 데이터의 편차를 잡아내기 위해서 입니다.
데이터를 변환했으면 이제 어떤 방향이 데이터의 편차를 가장 많이 잡아냈는지 알아봅니다. (쉽게 생각하면 2차원에서 데이터에 맞게 x축, y축을 설정하는 것을 말합니다)
먼저 방향 d(크기가 1인 벡터)가 주어졌다면, 행렬의 각 행 x는 d 방향으로 dot(x, d)만큼 늘어납니다. 그리고 0이 아닌 모든 벡터 w의 크기를 1로 변환하면, w는 방향을 나타냅니다
(여기서 이해를 못하실 수 있습니다. 일단 넘어가고 나중에 알려드겠습니다.)
def direction(w): mag = magnitude(w) return [w_i / mag for w_i in w]
def directional_variance_i(x_i, w): """w가 나타나는 방향에서 x_i행의 편차를 반환""" return dot(x_i, direction(w)) ** 2
def directional_variance(X, w): """w가 나타내는 방향에서 데이터 전체의 편차를 반환""" return sum(directional_variance_i(x_i, w) for x_i in X)
이제 이러한 편차를 최대화시키는 방향을 찾기 위해 경사 하강법을 사용하겠습니다.
def directional_variance_gradient_i(x_i, w): """방향의 경사(w의 기울기)에 x_i행이 기여하는 부분""" projection_length = dot(x_i, direction(w)) return [2 * projection_length * x_ij for x_ij in x_i]
def directional_variance_gradient(X, w): return vector_sum(directional_variance_gradient_i(x_i,w) for x_i in X)
이때 제1주성분은 directional_variance 함수를 최대화시키는 방향입니다.
def first_principal_component(X): guess = [1 for _ in X[0]] unscaled_maximizer = maximize_batch( partial(directional_variance, X), # w에 대한 함수 partial(directional_variance_gradient, X), # w에 대한 함수 guess) return direction(unscaled_maximizer)
만약 SGD(stochastic gradient descent)를 사용하고 싶다면
# 여기서는 "y"에 해당하는 값이 없기 때문에, "y"를 "None"으로 구성된 벡터로 대체하고 # 이 벡터를 무시하는 함수를 사용한다. def first_principal_component_sgd(X): guess = [1 for _ in X[0]] unscaled_maximizer = maximize_stochastic( lambda x, _, w: directional_variance_i(x, w), lambda x, _, w: directional_variance_gradient_i(x, w), X, [None for _ in X], guess) # 가짜 "y" return direction(unscaled_maximizer)
제1주성분에 해당하는 방향을 찾았다면, 데이터를 주성분에 투영시켜 해당 성분의 값을 찾습니다.
def project(v, w): """v를 w방향으로 투영""" coefficient = dot(v, w) return scalar_multiply(coefficient, w)
다른 성분들을 찾고 싶다면 이제 먼저 투영된 데이터를 제거해야 합니다.
def remove_projection_from_vector(v, w): """v에서 v를 w에 투영시킨 결과를 빼줌""" return vector_subtract(v, project(v, w))
def remove_projection(X, w): """X의 각 행을 w로 투영시키고 각 행에서 투영시킨 값을 빼줌""" return [remove_projection_from_vector(x_i, w) for x_i in X]
2차원 데이터를 사용했기 때문에, 데이터에서 제1주성분을 제거하면 남아 있는 데이터는 1차원입니다.
이제 remove_projection의 결과값으로 동일한 과정을 통해 다음 주성분을 찾습니다.
고차원 데이터에서는 이러한 반복적인 과정을 통해 원하는 만큼 많은 주성분을 찾을 수 있습니다.
def principal_component_analysis(X, num_components): components = [] for _ in range(num_components): component = first_principal_component(X) components.append(component) X = remove_projection(X, component) return components
계산된 성분을 사용하면 주어진 데이터를 저차원 공간에서 생성시킬 수 있다.
def transform_vector(v, components): return [dot(v, w) for w in components]
def transform(X, components): return [transform_vector(x_i, components) for x_i in X]
차원 축소가 중요한 이유 2가지
1. 차원 축소는 잡음(noise)에 해당되는 차원을 제거해 주고 밀접한 연된 차원을 합쳐 주면서 데이터 정제가 가능
2. 저차원으로 축소시킨 데이터에서는 고차원 데이터에서는 사용할 수 없었던 다양한 기법 사용 가능
단점
더 좋은 성능의 모형을 만들었지만, 만들어진 모형을 해석하는 것이 어려워짐
제0주성분이 증가할 수록 결과가 어떻게 된다라고 해석하는 것은 쉬운 일이 아니다!
댓글