본문 바로가기
연구관련/Bioinfo류

정규화(normalization)

by adnoctum 2010. 4. 19.



   많은 양의 데이터를 처리함에 있어 여러 이유로 정규화, 즉 데이터의 범위를 일치시키거나 분포를 유사하게 만들어 주는 등의 작업은 꼭 필요한 일이다. 


평균값을 이용한 정규화
중간값을 이용한 정규화
Quantile 정규화

평균값을 이용한 정규화

   데이터의 평균값을 0 으로 놓는 정규화는 일반적으로 원래의 데이터의 값의 분포가 '정규분포'임을 가정할 때가 많은데, 꼭 그렇지는 않아도 이와 같은 방법을 이용할 때가 있다. 이 때의 기본적인 아이디어는 '평균'에 해당하는 값은 0 으로, 그리고 평균에서 멀어질수록 값을 크게 주자, 는 것이다. 분산으로 나눈다는 의미는, 값의 분포가 고만고만한 상황에서 1이 차이나는 것과, 값의 분포가 매우 큰 경우에 1 차이나는 것은 분명 다른 경우이므로 분산으로 나눔으로써 원래 분포의 퍼짐에 의한 효과를 상쇄하고자 하는 것이다. 예를 들면,

1, 2, 3, 3, 2, 1, 2, 3, 4, 8, 1, 2 과 같은 데이터에서의 1과 3의 차이는
1002, 3929, 1949, 2212, 39, 2811, 7631, 9921, 877, 9929 에서의 9921과 9929 의 차이는

다를 것이라는 가정이다. 두 번째 데이터의 분산이 크기 때문에 9921과 9929는 별로 차이가 없는 값인 반면 첫 번재 데이터는 분산이 작고 그 와중에 2 가 차이가 나는 것이므로 1과 3은 분명 크게 차이가 나는 값인 것이다. 이와 같은 생각을 수식으로 표현하고자 분산으로 값을 나누어 주는 것이다. 일반적으로 이렇게 데이터에서 평균을 뺀 이후 표준편차로 나누어주는 작업을 z-transformation 이라고 하며, z-transformation은 데이터의 normal 분포를 가정하지 않는다. 평균값으로 정규화된 값은 다음과 같이 표현할 수 있겠다.




위의 식에서, 식 (1)의 di 위에 tilde(~)가 있는 것이 di 에 대한 정규화된 값이다. 이 값은, di 에서 d의 평균 E(d) 를 뺀 이후, d의 표준편차인 sigma_d 로 나누어 주면 된다. 이 과정을 C++ 코드로 나타내면 다음과 같다.



중간값을 이용한 정규화

   중간값을 이용한 정규화는 평균을 이용한 정규화의 절차와 거의 유사하나 데이터에서 값을 빼줄 때 (즉, 위의 식(1)에서) data의 평균 대신 중간값을 사용하는 것이다. 중간값을 이용한 정규화는 데이터에 너무 튀는 값이 있을 때 적당한 방법이다.  데이터의 중간값은 평균과는 달리 outlier에 영향을 받지 않는 성질을 갖고 있다. 평균이 아닌 중간값을 사용하는 이유는, 데이터에 너무 튀는 값, 즉 outlier 가 있을 것으로 보이는 경우에는 그와 같은 outlier가 되는 데이터 때문에 평균값이 잘못 구해질 수 있기 때문이다. 특히 이러한 경우는 data의 값이 작은 수로 나누어서 만들어지는 경우 자주 발생하는데, 분모가 너무 작으면 나누기를 행한 결과값 급격히 커지기 때문에 원래의 평균보다 큰 평균값이 구해질 수 있고, 이렇게 되면 전반적으로 데이터를 정규화시킨 값이 작게 나올 수 있게 된다. 데이터의 중간값(median)이란 N 개의 데이터를 오름차순으로 정렬하였을 때 [N/2] 에 위치하는 데이터의 값을 의미한다. 만약 N 이 홀수이면 N = 2k + 1 로 표시되는 자연수 k 에 대하여 k+1 번째 큰 값이 중간값이 되고, 만약 N 이 짝수라면 중간값을 결정하는 몇 가지 옵션이 존재하는데, 주로 N = 2k 를 만족하는 k에 대하여 k-1, k+1 번째로 큰 수의 평균을 중간값으로 사용할 수 있겠다. 이렇게 중간값을 구한 후 위의 식 1에서 E(d) 대신 그 중간값을 빼주면서 처리하면 중간값을 이용한 정규화가 된다. 따라서 식과 코드는 생략.



Quantile 정규화

   p quantile 이란 데이터를 오름차순으로 정렬하였을 때 p * data number 에 해당하는 데이터를 말한다. quantile 자체는 명확히 정의된 수학적 용어는 아닌 것 같으나, 일반적으로는 위의 의미로 사용되고 있다. quantile이 유용하게 사용되는 경우는, 주어진 분포가 가정하고 있는 분포와 유사한 것인지를 판단하는 Q-Q plot 을 이용하는 것이 있겠다. 흔히 말하는 '상위 몇 %' 에 해당하는 것이 quantile 과 비슷한 의미를 갖는다. Q-Q 정규화란 여러 데이터 set의 분포를 비슷하게 만들어 주는 작업으로,

data 1  D1 : d_11, d_12, ..., d_1N
data 2  D2 : d_21, d_22, ..., d_2M

인 두 data set D1과 D2가 있을 때, D1과 D2를 오름차순으로 정렬하였을 때 p%에 위치하는 데이터가 같은 값을 갖도록 해 주는 방법이 quantile 정규화이다. 좀 더 일반화시키면 D1, ..., DN 인 N 개의 data set이 있을 때, 각 data set의 p%에 위치하는 데이터의 값을 갖게 해 주는 작업이 quantile 정규화인 것이다. 각 데이터의

제일 작은 값들은 갖은 값을 갖고,
두 번째로 작은 값들이 같은 값을 갖고,
...
제일 큰 값이 같은 값을 갖도록 해 주는 작업.

이와 같은 작업은, 각 data set이 서로 다른 noise 에 의하여 분포의 전체 위치가 변할 가능성이 있는 경우 필요하다. 방법에 대한 아이디어는 매우 간단하여, 다음의 절차를 따른다.

i) N개의 각 데이터를 오름차순으로 정렬한다.
ii) 정렬된 데이터의 각 i 번째 데이터를 N 차원 상의 unit diagonal 직선으로 projection 시킨다.
iii) projection data 들에 대하여, 원래의 위치로 돌려 보낸다.

(방법상 오름차순이든 내림차순이든 상관은 없다. 이 글에서는 계속 오름차순으로 통일한다)
이 때 data 를 unit diagonal 로 projection 시킨다는 의미는, 2차원에 대해서 생각해 보면 (1,1)인 직선, N 차원에서 생각해 보면 (1, 1, ..., 1) 인 직선으로 데이터들을 projection 시킨다는 의미이고, projection 시키는 값은 주로 평균값을 사용한다. 그렇게 하여 새로운 값이 할당되면, 그 값을, 원래의 값이 원 데이터에서 위치하던 곳으로 옮겨 준다. 지금까지의 상황을 그림으로 나타내면 다음과 같다.



원래의 데이터가 위와 같은 모습이었다고 하자. 원 데이터들을 크기 순으로 정렬을 하면 다음과 같은 모습이다.



정렬된 데이터에서, 정렬된 순위가 같은 데이터들이 같은 값을 갖도록 할 것이다. 아래와 같이 다시 데이터들만 따로 뽑아서 살펴 보면,




새로운 값은, 각각의 정렬된 순위에 해당하는 데이터들의 '평균값'으로 설정한다. 이렇게 설정된 새로운 값으로 원래의 값들을 대체한다. 아래와 같이.




다시, 변환된 값의 이전 값이 원 데이터에서 차지하던 순위로 지금 변환시킨 값들을 위치시킨다. 이것은 즉, 변하기 전의 데이터의 값이 변한 후의 데이터의 값으로 대체되는 것을 의미한다. 따라서 결과적으로 최종 데이터는 아래와 같은 모습으로 바뀐다.





데이터를 각각 오름차순정렬하여 같은 순위에 해당하는 데이터를 좌표상에 찍는 Q-Q plot 을 그려 보면 quantile 정규화의 효과를 쉽게 알 수 있다.

Q-Q plot before Q-정규화Q-Q plot before Q-정규화Q-Q plot after Q-정규화Q-Q plot after Q-정규화



지금까지의 상황을 C++ 코드로 나타내면 다음과 같다.



몇 가지 관련 얘기들

   때에 따라서는 정규화(normalization)과 평균화(standardization)을 구분하여 사용하는 경우도 있다. 평균화는 모든 데이터의 값을 [0, 1] 의 구간으로 한정시키는 것으로, 다음과 같은 식을 사용할 수 있겠다.



이에 대한 C++ 코드는 다음과 같다.



위와 같은 절차를 수행함에 있어 원래의 데이터를 면밀히 검토하여 어떠한 방법을 사용할 것인지를 결정해야 하며, 데이터의 특성에 상관없이 모두 적용할 수 있는 방법은 없는 것으로 보인다. 일반적으로 평균값을 이용한 정규화를 많이 이용하는데, 이럴 때 data에 약간의 noise가 있을 경우, 상/하위 1% 정도를 없애는 전처리 작업 후 정규화를 할 수도 있다. 데이터의 상/하위 p %를 버리는 코드는 C++로 보면 다음과 같다.





ps. 에러 처리는 하지 않는다. 실제 코드에서는 위의 루틴 들어 가기 전에 왠만한 에러 처리를 다 한다. container가 비어 있던가, max_value - min_value 가 너무 작다던가, 하는 등의 에러 처리가 포함된다.