Corgi Dog Bark

ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 역변환 방법( Inverse CDF Technique)
    머신러닝(MACHINE LEARNING)/통계(Statistics) 2021. 6. 8. 14:36
    반응형

    0. 서론


    - 컴퓨터를 쓰다 보면, 생각보다 난수를 일으킬 일들이 많이 있다. 이럴때, 보통 python 의 경우, random.random( ) 모듈을 사용하게 되면, 0~1 사이 분포의 난수가 일어나게 되는데, 그렇다면, 어떤 특정한 함수를 따르는 난수를 만들어 내고 싶다면, 어떻게 해야 할까? 라는 의문에서 이 역변환 방법(inverse CDF technique) 이 쓰이게 된다.

     

     

     

     

     

    1. 기본적인 난수 생성기


    - 앞서 말했듯, 가장 기본적인 난수 생성기는 균등분포(uniform dist.) 난수생성기이다. 따라서 이것만 가지고는 0~1 사이의 난수만 생성할 수 있지, 다른 특정한 PDF 를 가지는 난수를 생성하기는 힘들다. 하지만, 역변환 방법(Inverse CDF) 을 사용하게 된다면, 우리는 어떠한 분포를 따르는 난수생성기를 무조건 만들어 낼 수 있다는 것이다..

     

     

     

     

     

    2. 균등분포


    - 우리가 알고 있는 확률 분포 함수(PDF) 는 모두 적분해서 1이고, 이를 활용하여 누적분포 함수(CDF) 를 만들게 된다면, \(F_{X}(x) = P[X<=x] \) 를 만족시키는 누적분포함수를 만들 수 있다. 

     

    - 이때 우리의 누적분포 함수 CDF \(F_{X}(x) \) 은 범위가 0~1 을 만족시키고, 우리가 가지고 있는 기본 난수 생성기 또한 0~1 범위 사이의 분포에서 난수를 발생시킨다. 이 두개의 범위가 같으므로 우리는 뭔가 이를 활용하여, 특정분포를 나타내는 난수를 만들 수 도 있겠는데? 라는 생각이 들게 된다.

     

    - 이를 보이기 위해, 먼저 우리가 특정 밀도 함수(PDF)를 따르는 확률 변수 X 에 대해, 누적확률 분포를 나타내주면 , \( F_{X}(x) \) 가 되게 된다. 

     

    - 이때, Uniform_dist. 를 따르는 U에 대해 \( U \sim UNIF(0,1) \) 의 함수로 정의 되는 \( Y = F_{X}^{-1}(U) \) 를 나타내어 줄 수 있게 된다. 좀 더 쉽게 말하면, \( F_{X}(x) \) 의 범위가 0~1 사이 이므로, 역함수를 취해주게 되면, 0~1 사이의 범위의 U값에 대해 \( Y = F_{X}^{-1}(U) \) 를 만족시키는 Y가 있을 수 있다는 것이다.

     

    - 이때 생성된 Y 가 확률변수 X 와 동일한 분포를 따르게 되어 Y를 우리가 원하는 함수의 난수로 이용할 수 있다는 것인데, 이를 증명해보자. 

     

    \begin{align*} 
        F_{Y}(y) = P[Y <= y] \\ 
        = P[F_{X}^{-1}(U) <= y] \\
        = P[U <= F_{X}(y)] \\
        = F_{X}(y)
    \end{align*}

     

    - 다시 한번 정리하자면, CDF 는 0~1 사이의 값을 가지고 있고, 우리의 랜덤난수기는 0~1 사이의 랜덤 분포기를 생성시키므로, 우리의 목표는 이 랜덤 난수기를 가지고 특정확률 분포를 만족시키는 랜덤난수기로 가야한다는 것이다. 따라서 , \( Y = F_{X}^{-1}(U) \) 의 역함수를 가지고, 난수를 만들어주면 된다는 것이 결론이다.( Y가 X 와 동일한 분포를 가지는 것에 대해서는 위에 증명을 해놓았다.)

     

     

     

     

     

    3. 난수 생성 해보기


    - 우리가 원하는 함수가 만약 \(F(x) = \frac{e^{-x}}{(1+e^{-x})^2} \) (pdf of Logistic Func.)라고 했을 때, 이 F(x)를 만족시키는 난수를 어떻게 생각해야 할지 막막 하겠지만, 위의 Inverse CDF 를 적용시켜보자.

     

    - 우선 누적 분포 함수는 \(F(x) = \frac{e^{x}}{1+e^{x}} \) (Logistic CDF Func.)를 만족시키게 되고, 이에 대한 역함수는, \(F^{-1}(U) = log(\frac{U}{1+U}) \) 이 되게 된다.

     

    - 따라서 이 식을 따라 파이썬 코드를 만들어 주게 되면,

    import math
    import random
    import scipy.stats
    import numpy as np
    
    # 로지스틱함수의 cdf 의 역함수
    def inv_logistic_df(u):
        return math.log(u / (1 - u))
    
    # 램덤 난수에 의한 inv_logistic_df 구해주기
    random_num = [random.random() for _ in range(10000)]
    random_nums_from_logistic = [inv_logistic_df(ran_num) for ran_num in random_num]
    
    # 여기는 random_num을 함수로 표현해주기 위해서 정리.
    hist = np.histogram(random_nums_from_logistic)
    hist_dist = scipy.stats.rv_histogram(hist)
    
    # 본래의 함수 logistic func 을 나타내기 위한 x,y 설정 즉 , f(x) = x 여기서의 f(x) 는 pdf(x)이다.
    x = np.linspace(-10,10)
    y = np.exp(-x)/((1+np.exp(-x))**2)
    
    plt.hist(random_nums_from_logistic,density = True, bins = 50, label = "random_number")
    plt.plot(x,y, label = "logistic pdf")
    plt.legend()
    plt.show()
    
    
    # 본래의 함수 logistic func 을 나타내기 위한 x,y 설정 즉 , f(x) = x 여기서의 f(x)는 cdf(x) 이다.
    x = np.linspace(-10,10)
    y = np.exp(x)/(1+np.exp(x))
    
    plt.plot(x,y,label = "logistic cdf")
    plt.plot(x,hist_dist.cdf(x),label = "random number")
    plt.legend()
    plt.show()

    - 다음과 같이 거의 비슷한 형태의 그래프가 나오는 것을 확인 가능하다. 따라서 우리가 생성한 난수분포기는 Logistic 분포를 따르는 함수라 봐도 무방하겠다..

    - (참고) 여기서의 logistic PDF 는 Logistic 함수를 써서 만든 것이 아닌, Random.random() 을 사용한 뒤 Inverse CDF 를 통해 만들어 낸 랜덤 분포이다.

     

     

    4. 난수 생성해보기 2


    - 이제 원안에 랜덤하게 점을 찍는다고 생각해보자. 그렇게 되면 우리는 \(F(d,\theta)\) 로 식을 표현할 수 가 있는데, 

     

    - 일반적으로 반지름이 1인원안에 찍는다 생각하면, \( \theta \) 만 신경써주고, d 는 그냥 난수분포기에서 나온값 사용하면, 되는거 아냐 ? 라고 했을 시 다음과 같은 오류가 발생한다. 

    - 다음과 같이 중앙에 점이 몰리게 되면서, 우리가 원하는 값과는 많이 멀어진 결과값을 가지게 된다.

     

    - 따라서, 이러한 원의 CDF 를 먼저 구해주어, 그다음 Inverse cdf trans. 를 적용해주어야 본 값을 얻게 되는데, 이 함수의 cdf 를 구해주게 되면 \( F_{X}(x) = \frac{d^2}{r^2} \) 이 되고,

     

    - 이에 역함수를 구해주게 되면, \(Y = r*(U^{0.5}) \)  가 되게 된다. 이를 구현하면 다음과 같이 정상전개가 되게 된다.

     

    - 이를 구현한 코드는 다음과 같다. 

    import turtle
    import math
    import random
    
    wn = turtle.Screen()
    turtle.tracer(8,0)
    alex = turtle.Turtle()
    alex.hideturtle()
    r = 200
    
    for i in range(5000):
        u = random.random()
        # d = r*(u) 일반적으로 생각하기에 쉬운 오류
        d = r*(u**0.5)
        theta = random.random()*360
        x = d * math.cos(math.radians(theta))
        y = d * math.sin(math.radians(theta))
        alex.penup()
        alex.setposition(x,y)
        alex.dot()
    turtle.update()
    wn.mainloop()

     

    5. - 프로그래머스 인공지능 데브코스 참조.

    반응형

    댓글

Designed by Tistory.