ch 2. Introduction: Models we believe in

|
  • data를 통해 우리의 믿음을 수정
    • data의 variance가 작고 믿음의 불확실성이 작은 곳에서는 decision이 쉽다.
      • ex> 주행 중에 신호등을 관찰하고 적색인지 녹색인지 판단
    • 반대로, data의 variance가 크거나 믿음의 불확실성이 큰 부분에서 수학이 도움이 된다.

2.1. Models of observations & models of beliefs

  • 동전을 던져서 앞뒷면을 관찰하는 예제
    • model of observations
      • 매 flip시 bias가 같다/이전 flip 기억 못함
    • model of beliefs
      • coin이 공정할 것이나 어느정도는 bias될 수 있다.

2.1.1. Models have parameters

  • 어떤 지역의 비가올 확률을 알고싶다.
    • input: 지역의 고도
    • output: 비가 올 확률
    • 이라하면 input으로 output을 도출해야하는데, 이 때 영향을 미치는 정도를 parameter라 한다.
  • 동전 던지기 예제에서, 동전을 반으로 자르고 (뒷면의 무게-앞면의 무게)를 lopsidedness라고 해보자.
    • 2.png
    • 동전의 기울기에 따라 앞면이 나올 확률이 달라진다고 모델링을 했는데, $\gamma$는 그 모델의 파라미터이다.
      • $p(\gamma)$:$\gamma$값의 candidate들을 얼마나 믿을 것인가에 대한 확률.
        • 여기서 $\alpha$는 hyperparameter이다.

2.1.2. Prior & posterior beliefs

  • prior: 단순히 어떤 특정 data를 배제한 믿음
  • posterior: data를 include한 믿음

2.2. Three goals for inference from data

  • Estimation of parameter values
    • model과 데이터가 주어졌을 때, 가능한 parameter value들을 어느정도씩 믿어야하나?
  • Prediction of data values
    • 현재 belief로 다른 data를 prediction
    • Bayesian inference에서는 믿음의 weighted average로 prediction이 된다.
  • Model comparison
    • 모델이 여러개 있을 때, 하나씩 최적화해보고 좋은 모델 쓴다… 고얘기지 뭐…

2.3. R programming language -생략-

Dynamic programming

|

언제 적용이 가능한가?

  • Optimal substructure(가능성 증명)
    • optimal solution of subproblem is in optimal solution of problem.
  • recursive하게 구현했을 때, 중복호출이 일어난다.(효율성 증명)

예시

  • 피보나치 수열 구하기
  • 행렬 경로문제

이 중에서 피보나치는 너무 쉬우니 행렬 경로문제를 풀어보자!

행렬 경로문제

문제 정의

문제는 다음과 같이 정의된다.

  • 양의 숫자가 쓰여진 nxn행렬이 존재.
  • 행렬의 좌상단에서 시작해서 우측 하단에 도착하는 최대(최소) 스코어를 구하는 문제.
  • 우측, 하단으로만 이동 가능하다.

만약 좌측, 상단으로 이동가능하면 안된다. 이는 나중에 고민해보자

이 문제는 DP로 풀 수 있다. 증명해보자

Optimal substructure 증명

(0, 0)~>(i, j)까지의 최적 해를 S[i][j]라 하자. 이 때 S[i][j]에 포함된 임의의 경로 (0, 0)~>(x, y) (x<=i, y <=j, x+y < i+j)S[x][y]보다 작은, 즉 최적해가 아니라고 가정해보자. 이렇게 가정했을 때 모순이 있음을 밝히면, S[i][j]는 모든 subproblem의 최적해를 가진다고 볼 수 있다.

모순점) (x, y)까지의 경로가 최적해가 아니라면, S[x][y]로 바꾸면 (i, j)의 경로에 대해 더 큰 최적의 해를 가질 수 있으므로 모순된다. 따라서 Optimal substructure임을 알 수 있다.

recursive하게 구현했을 때, 중복호출이 일어나는가?

  • S[i][j] = max(S[i-1][j], S[i][j-1]) + m[i][j]
  • S[i-1][j] = max(S[i-2][j], S[i-1][j-1]) + m[i-1][j]
  • S[i][j-1] = max(S[i-1][j-1], S[i][j-2]) + m[i][j-1]

즉 중복호출이 일어남을 알 수 있고, 따라서 DP를 적용하면 효율이 좋아진다.

이제 DP로 풀어보자.

풀기

코드를 쓰자

import numpy as np

def getHighestScore(mat, scores, endTup):
    x, y = endTup
    if scores.item(x, y) != 0: # score가 존재한다면 return
        return scores.item(x, y)
    else:
        score = max(getHighestScore(mat, scores, (x-1, y)),
                    getHighestScore(mat, scores, (x, y-1))) + mat.item(x,y)
        scores.itemset((x, y), score)
        return score


def initializeScoreMat(mat):
    rows, cols = mat.shape[:2]
    scoreMat = np.zeros((rows, cols), dtype=np.uint8)
    scoreMat.itemset((0, 0), mat.item(0, 0))
    for i in xrange(1, rows):
        score = mat.item(i, 0) + scoreMat.item(i-1, 0)
        scoreMat.itemset((i, 0), score)
    for i in xrange(1, cols):
        score = mat.item(0, i) + scoreMat.item(0, i-1)
        scoreMat.itemset((0, i), score)
    return scoreMat


if __name__ == '__main__':
    testList = [[6, 7, 12,  5],
                [5, 3, 11, 18],
                [7, 17, 3,  3],
                [8, 10, 14, 9]]
    testMat = np.asarray(testList, dtype=np.uint8)

    scoreMat = initializeScoreMat(testMat)
    score = getHighestScore(testMat, scoreMat, (3, 3))

    print("score")
    print(score)

조약돌 놓기 문제

문제 정의

  • n*3 테이블의 각 칸에 숫자가 쓰여있다.
  • 가로나 세로로 인접한 두 칸에 조약돌이 놓일 수 없다.
  • 각 row에는 하나 이상의 조약돌을 놓는다.

이 때 한 행에 놓을 수 있는 패턴은 다음 네가지이다.

# pattern
1 100
2 010
3 001
4 101

최대 점수 정의

이 문제에서는 Optimal substructure를 증명하기 전에 최대 점수를 먼저 정의하도록 한다.

$m_{i,1}$ : $i$번째 행의 패턴 1일 때의 점수. $s_{i,j}$ : $1$행부터 $i$행까지 돌을 놓는데, $i$번째 돌을 패턴$j$로 놓았을 때의 최대 점수. $s_i = max_j(s_{i,j})$ : $1$행부터 $i$행까지의 최대 점수. 우리가 구하고 싶은 최대 점수는 $s_i$이다.

Optimal Substructure 증명

먼저 다음과 같이 정의한다.

  • $s_i$ : i까지의 최적해,
  • $s’_i$ : i까지의 최적해가 아닌 어떤 해

$s_i$를 구하는 것은 모든 패턴에 대해 $s_{i,j}$를 구하는 것과 동치이다. (max 한번만 취하면 되니까) $s_{i,1}$에 대해서 살펴보면 i번째 패턴이 100이므로 i-1번째 패턴은 010, 001 두가지가 올 수 있다.

(둘 중 하나만 최적해가 아니라도 되지만…)

만약 이 중에 가 선택되었고 이는 사실 최적 해가 아니라고 해보자. 그러면 당연히 으로 바꾸면 해가 커지므로 모순이 생긴다.(3을 선택해도 똑같이 증명가능) 따라서 증명 끝!

recursive하게 호출할 때 중복이 일어나는가?

나머지에 대해서 똑같이 적용하면

  • $s_{i,1} = max(s_{i-1,2}, s_{i-1,3}) + m_{i,1}$
  • $s_{i,2} = max(s_{i-1,1}, s_{i-1,3}, s_{i-1,4}) + m_{i,2}$
  • $s_{i,3} = max(s_{i-1,1}, s_{i-1,2}) + m_{i,3}$
  • $s_{i,4} = s_{i-1,2} + m_{i,4}$

이다. 따라서 중복호출!

Gaussian Process

|

Supervised learning 타입

Supervised learning을 두 가지 타입으로 나눠볼 수 있다.

  • 고려할 함수들의 class를 제한하는 방법(예> linear function with some degree)
    • 함수의 richness를 잘 살려야 함
      • 너무 rich하게 잡으면 overfitting
      • 너무 작게 잡아버리면 poor prediction.
  • 모든 가능한 함수의 prior probability를 주는 방법
    • 이 쪽이 Gaussian process

Bayesian linear model

아주 쉬운 예제로 Linear model이 다음처럼 있다고 가정하자.

where $\epsilon \sim N(0, \sigma_n^2)$

  • w의 prior distribution을 가정(Bayesian…)
    • $\textbf{w} \sim N(\textbf{0}, \Sigma_p)$
  • likelihood
    • $p(\textbf{y}\vert \textbf{X}, \textbf{w}) = N(\textbf{X}^T\textbf{w}, \sigma_n^2I)$
  • posterior
    • $p(\textbf{w}\vert \textbf{X},\textbf{y}) \sim N(\frac{1}{\sigma_n^2}A^{-1}\textbf{X}\textbf{y},\ A^{-1})$
    • $A=\sigma_n^{-2}XX^T+\Sigma_p^{-1}$
  • prediction

LR with Bayes.png Observation에 의해서 posterior를 구할 수 있었으며, 위의 prediction 식을 사용하여 새로운 y값을 구할 수 있다. 이제 linear model이 아닌 kernel trick을 쓰면 Gaussian process!

RANSAC - RANdom Sampling And Consensus

|

RANSAC: RANdom Sampling And Consensus

뭐에 쓰는 물건인고?


  • 목적
    • 어떤 관측값들에 대해 모델의 파라미터를 추정하고싶다.
  • 문제점
    • 관측값에 노이즈가 끼어있다.
    • 측정오차가 심해서 모델을 예측하는데 방해되는 관측값(outlier)이 존재한다.

예제를 중심으로 설명하기 위해 포물선에 대한 어떤 관측값들을 얻었고, 이를 통해 포물선 모델의 parameter를 정한다고 가정해보자.

데이터 만들기


여기서는 오차가 심하지 않은 데이터(1)와 심한 데이터(2)를 만들어본다.

import numpy as np
import matplotlib
import matplotlib.pyplot as plt

a_true = -1
b_true = 8
c_true = 1
num_samples = 100

def good_data(sigma=1, num_samples=100):
    x = np.linspace(0.0, 10.0, num_samples)
    y = a_true*x*x + b_true*x + c_true \
        + np.random.normal(0, sigma, num_samples)
    return x, y

def bad_data(sigma=1, num_samples=100):
    x = np.linspace(0.0, 10.0, num_samples)
    # noise 생성!
    noise = np.zeros(num_samples)
    x_left_idx = x > 4
    x_right_idx = x < 6
    x_idx = x_left_idx & x_right_idx
    noise[x_idx] = -10
    
    y = a_true*x*x + b_true*x + c_true \
        + np.random.normal(0, sigma, num_samples) + noise
    return x, y

x_good, y_good = good_data(num_samples=num_samples)
x_bad, y_bad = bad_data(num_samples=num_samples)

plt.figure(1, figsize=(8, 3))
plt.subplot(121)

plt.scatter(x_good, y_good)
plt.title('good_data')
plt.xlabel('x')
plt.ylabel('y')

plt.subplot(122)
plt.scatter(x_bad, y_bad)
plt.title('bad_data')
plt.annotate('noise',  # noise 낀 곳을 표시
             fontsize=20,
             xy=(4, y_bad[int(num_samples/10)*4]),
             xytext=(8, 2),
             arrowprops=dict(facecolor='red', shrink=0.05))
plt.show()

output_1_0.png

위의 두 sample을 가지고 실험해보자!

residual 최소화


가장 쉬운 예제로 $\sum residual^2$을 최소화하도록 포물선을 근사시켜보자.

여기서는 np.polyfit이라는 함수를 써서 진행한다. Appendix에서 직접 만들어보자…

def get_y(x, a, b, c):
    return a*x*x + b*x + c

a1, b1, c1 = np.polyfit(x_good, y_good, 2) # coefficient를 내주는 함수
y_good_pred = get_y(x_good, a1, b1, c1)

a2, b2, c2 = np.polyfit(x_bad, y_bad, 2)
y_bad_pred = get_y(x_bad, a2, b2, c2)


plt.figure(1, figsize=(8, 3))
plt.subplot(121)

plt.scatter(x_good, y_good)
plt.plot(x_good, y_good_pred, 'r')
plt.title('good_data_pred')
plt.xlabel('x')
plt.ylabel('y')

plt.subplot(122)
plt.scatter(x_bad, y_bad)
plt.plot(x_bad, y_bad_pred, 'r')
plt.title('bad_data_pred')
plt.show()

print("true parameter : {}, {}, {}".format(a_true, b_true, c_true))
print("predicted parameter good : {}, {}, {}".format(a1, b1, c1))
print("predicted parameter bad : {}, {}, {}".format(a2, b2, c2))

output_4_0.png

true parameter : -1, 8, 1 predicted parameter good : -1.015918507583149, 8.127778218761788, 0.9441332273227827 predicted parameter bad : -0.7282561048930103, 5.312333174153526, 3.4425031808102498

위 그림과 추정된 파라미터들을 보면 good에서는 그럴싸하지만, bad에서는 상당히 파라미터를 잘못 추축하고있는 것을 볼 수 있다. 이제 드디어 RANSAC 얘기로 넘어가보자.

RANSAC 알고리즘


가장 많은 수의 데이터로부터 지지받는 모델을 선택하는 방법. 방법은 다음과 같다.

  1. max_inlier = 0으로 초기화한다.
  2. 무작위로 세 점을 뽑는다.(parameter를 만들 때 필요한 최소 갯수의 observation)
  3. 2에서 뽑은 점으로 model을 만든다 = parameter setting
  4. 3에서 만든 모델에서 예측한 값과 일정 threshold안에 있는 inlier들의 갯수를 센다.
  5. 4에서 센 갯수가 max_inlier보다 크면 max_inlier를 갱신하고, model을 저장한다.
  6. 2~5를 N번 반복한 후 최종 저장된 model을 바노한한다.
  7. (optional) 최종 inlier로 뽑힌 애들로 model을 refine한다.

이제 코드로 살펴보자

def RANSAC(x, y, threshold = 0.3, N = 10):

    # (1)
    max_inlier = 0
    a, b, c = 0, 0, 0
    inlier = None  # 나중에 쉽게 하려고 inlier도 저장한다.
    
    # (6)
    for i in range(N):
        # (2)
        samples = np.random.uniform(0, num_samples, 3)
        samples = [int(sample) for sample in samples]
        x_sampled = x[samples]
        y_sampled = y[samples]
        
        # (3)
        a_tmp, b_tmp, c_tmp = np.polyfit(x_sampled, y_sampled, 2)
        y_pred = get_y(x, a_tmp, b_tmp, c_tmp)
        
        # (4)
        tmp_inlier = abs(y_pred - y) < threshold
        count_inlier = len(x[tmp_inlier])  # 꼼수...

        # (5)
        if count_inlier > max_inlier:
            max_inlier = count_inlier
            inlier = tmp_inlier
            a, b, c = a_tmp, b_tmp, c_tmp
    # (7)
    a, b, c = np.polyfit(x[inlier], y[inlier], 2)
    return a, b, c

알고리즘대로 만들었고… 이제 실행시켜보자

a_good, b_good, c_good = RANSAC(x_good, y_good)
a_bad, b_bad, c_bad = RANSAC(x_bad, y_bad)

y_bad_pred = get_y(x_good, a_good, b_good, c_good)
y_bad_pred = get_y(x_bad, a_bad, b_bad, c_bad)

plt.figure(1, figsize=(8, 3))
plt.subplot(121)

plt.scatter(x_good, y_good)
plt.plot(x_good, y_good_pred, 'r')
plt.title('good_data_pred')
plt.xlabel('x')
plt.ylabel('y')

plt.subplot(122)
plt.scatter(x_bad, y_bad)
plt.plot(x_bad, y_bad_pred, 'r')
plt.title('bad_data_pred')
plt.show()

print("true parameter : {}, {}, {}".format(a_true, b_true, c_true))
print("predicted parameter good : {}, {}, {}".format(a_good, b_good, c_good))
print("predicted parameter bad : {}, {}, {}".format(a_good, b_good, c_good))

output_9_0.png

true parameter : -1, 8, 1 predicted parameter good : -0.9843167682128994, 7.790038906293564, 1.3230583655902022 predicted parameter bad : -0.9843167682128994, 7.790038906293564, 1.3230583655902022

outlier를 제거하고, 잘 나오는 것을 알 수 있다. 그러면 파라미터는 어떻게 최적화할 수 있을까?

parameter 최적화


N 최적화(inlier 비율을 알고 있을 때..)

RANSAC이 성공하려면, N번의 시도 중, 적어도 한번 inlier들에 대해서만 샘플 데이터가 뽑혀야한다. N을 키우면 그럴 확률이 커지지만 확률이 크도록 적당한 N값을 찾아서 수행시간을 줄여야함!

  • $N$: 반복 횟수
  • $m$: 한번에 뽑는 sample 수
  • $a$: observation들 중 inlier의 비율

이라 하면 $N$번중 적어로 한번은 inlier에서만 뽑힐 확률 $p$는 다음과 같다.

샘플링하는 수가 observation 갯수보다 현저히 작다고 가정하면 $a^m$은 inlier에서 전부 뽑을 확률이다. 따라서 $1-a^m$은 inlier에서 전부 뽑히지는 않을 확률이며 이를 N번 수행했을 때이므로, 위와 같은 식이 된다.

이를 토대로 N에 대한 식으로 바꿔보면 다음과 같다. 여기에, RANSAC이 성공할 확률($p$)을 99.9로 놓고, $a$는 위의 예제에서 0.8이 되므로, N을 구해보면, 이 된다.

threshold 최적화

inlier들의 residual의 표준편차를 $\sigma$라 할 때, threshold를 $2\sigma$ 또는 $3\sigma$로 놓는 것이 좋다. 이 때, inlier들의 residual이 정규분포를 따른다고 가정하면 $2\sigma$는 97.7%, $3\sigma$는 99.9%의 inlier를 포함하게된다.

다른 robust 파라미터 추정


RANSAC처럼 데이터 중에 outlier가 있어도 추정을 잘 하는 녀석들을 robust parameter estimation algorithm이라 한다.

이 중에 RANSAC 말고도

  • M-estimator
  • LMedS

등이 있다.

RANSAC은 threshold 안에 들어가는 녀석들은 전부 error가 없다고 가정하는데, 이를 residual error로 일정부분 상쇄시키거나 한다. 나중에 필요하면 찾아보자! IMAGE

RANSAC의 한계


  1. N을 아무리 늘려도 해를 못찾을 수 있다.
  2. outlier가 어떤 구조를 가지고있거나, 비율이 굉장히 많으면 outlier의 분포를 근사시킬 수 있다.
  3. parameter를 뽑기위한 sample 갯수를 최소화하는 방법은 noise를 포함한다. 따라서 인접한 sample들을 뽑으면 오차가 엄청 커지지.. 이를 위해 sample을 적당한 구간별로 나눠서 뽑는 방법이 있으면 좋겠다.

GPyOpt - 처음 tutorial 진행

|
from GPyOpt.methods import BayesianOptimization
import numpy as np

# --- Define your problem
def f(x):
    return (6*x - 2)**2 * np.sin(12 * x - 4)
bounds = [(0,1)]

# --- Solve your problem
myBopt = BayesianOptimization(f=f, bounds=bounds)
myBopt.run_optimization(max_iter=16)
myBopt.plot_acquisition()

output_0_0.png

print(myBopt.x_opt)
print(myBopt.Y_new)
[ 0.75506832]
[[-5.66412991]]

domain 여러개 확인..

tensorflow에 적용하기 전에, discrete한게 되는지 봐야함.

from GPyOpt.methods import BayesianOptimization
import numpy as np

# --- Define your problem
def f(x):
    return (6*x[0,0] - 2)**2 * np.sin(12 * x[0, 1] - 4)

domain = [{'name': 'a',
           'type': 'continuous',
           'domain':(0,10)},
          {'name': 'test',
           'type': 'discrete',
           'domain':(0,1,2,3,4,5,6,7,8,9,10),
           'dimensionality': 1}]

# --- Solve your problem
myBopt = BayesianOptimization(f=f, domain=domain)
myBopt.run_optimization(max_iter=5)
myBopt.plot_acquisition()
myBopt.X
array([[  2.0503028 ,   6.        ],
       [  2.44130349,   4.        ],
       [  5.74897004,   7.        ],
       [  2.66912911,   9.        ],
       [  6.31059492,   8.        ],
       [  7.10601309,   7.        ],
       [  8.53670111,   6.        ],
       [ 10.        ,   5.        ],
       [  9.49270161,   7.        ],
       [ 10.        ,   8.        ]])
myBopt.x_opt
array([ 9.49270161,  7.        ])
myBopt.Y
array([[ -9.52947568e+01],
       [  2.83173048e+00],
       [ -1.04939570e+03],
       [ -6.31711134e+01],
       [ -1.00254584e+03],
       [ -1.64119927e+03],
       [ -2.17534541e+03],
       [ -1.75449757e+03],
       [ -3.00172758e+03],
       [ -2.62212386e+03]])