본문 바로가기

수학/수치해석

[수치해석 10] 최소 제곱 회귀 분석

곡선 적합(Curve Fitting)

어떤 상황에서 데이터는 이산적인 값으로 주어진다. 이때, 우리는 이 데이터들간의 연관성에 대해 궁금하거나, 해당 데이터들을 기반으로 새로운 임의의 점을 추정하고 싶을 수 있다. 곡선 적합은 중간이 되는 추정치를 구하기 위해 특정 데이터들을 곡선에 적합시키는 것으로, 현실의 복잡한 데이터로부터 이들에 적합하는 간단한 함수를 유도하는 것이다. 즉, 데이터들 사이의 연관성을 곡선 형태로 도출하고, 이를 통해 새로운 값을 추정하기 위한 것이다.

데이터간의 오차가 큰 상황에서는 최소제곱회귀분석을 사용한다. 이 경우.

  • 데이터의 일반적인 경향성을 나타내는 것으로, 단일 곡선을 유도한다.
  • 유도된 곡선이 꼭 모든 점을 통과하지는 않는다. (도출되는 곡선은 근사치를 추정한다.)

데이터가 정확하게 알려진 경우에는 보간법을 사용한다.

  • 각 점을 직접 통과하는 곡선 또는 일련의 곡선을 구한다.
  • 잘 알려진 이산적 점들의 사이 값을 추정하게 된다.

곡선 적합에 이용할 수 있는 방법들

 

Least-squares regression

점들 사이의 추세를 나타내는 방식. 모든 점이 일치하지는 않을 수 있다.

 

Linear interpolation

점 사이를 선형으로 연결하는 방식. 다른 데이터가 왔을 때 오차가 클 수 있다.

 

curvilinear interpolation

점들 사이를 곡선 형태로 연결하는 방식. 계산량이 많아질 수 있다.

 

통계학 복습

 

표본 :  모집단(대상이 되는 전체 집합)의 부분집합.

 

산술 평균(arithmetic mean) : 표본의 총 합을 표본의 개수 n으로 나누는 방식의 평균.

 

표준편차(standard deviation) : 표본의 분포도에 대한 척도.

 

분산(variance) : 표준편차의 제곱.

자유도(degrees of freedom)

위 등식은 성립한다. 이때 n개의 원소 중 n-1개의 값이 설정되면 등식의 총 합은 0이므로, 나머지 하나의 값은 반드시 정해진다. 따라서 n-1개의 값만이 자유롭게 결정될 수 있다. 이때 n-1 을 자유도라고 한다.

 

최소제곱 회귀분석(Least-Squares Regression)

데이터 안에 상당한 오차가 있다면, 각각의 점에 맞추기 보다는 일반적인 경향성을 나타내는 편이 더 좋다. 이때 최소제곱을 이용하여 회귀 분석하는 방식이 최소제곱 회귀분석이다.

선형 회귀분석

최소제곱근사의 가장 간단한 방식으로, 일차식 형태로 주어진 데이터들을 근사한다.

기존에 관측한 관측치 (x1,y1), (x2, y2), (x3, y3), ... (xn, yn) 을 직선에 적합하면,

y = a0 + a1x + e

  • a0 : 절편
  • a1 : 기울기
  • e :  잔차(residual), 모델과 관측치 사이의 오차.

e = y - a0 - a1x 이므로, 잔차 e는  참값(y)과 선형방정식에 의해 예측되는 근사값 (a0 + a1x) 사이의 차이다.

최적 적합에 최소제곱을 이용하는 이유

최적 적합의 결과로는 하나의 곡선이 결정되어야 한다. 이때 적합에는 다음 방식들이 고려될 수 있다.

  • 잔차의 합을 최소화
  • 잔차의 절대값의 합을 최소화
  • 최소 최대 판별
  • 잔차의 제곱 합을 최소화

위 적합 방법에 대해 최소제곱법이 최선인 이유를 생각해보자.

잔차의 합을 최소화

 

특정 두 점을 지나는 직선에 대해 두 점 사이의 중심점을 지나는 직선을 생각해보자. 두 점은 해당 점에 대해 대칭이므로 해당 점을 지나는 수직선이 아닌 어떤 직선에 대해서도 잔차의 합은 0이 되므로, 단순 덧셈은 사용 불가능하다.

잔차의 절대값 합을 최소화

두 직선 L이 교차하는 점 a에 대해 거리가 동일한 두 수평선 H을 생각해보자. 이때 a와 d1d2 선분을 지나는 직선 K가 d1d2와 교차할 때 잔차의 절대값 합인 |(d2.y - k.y)| + |(d1.y - k.y)| =  d2.y-d1.y + k.y - k.y = d2.y - d1.y 로 동일하므로, K는 점 d1, d2 혹은 d3, d4에 대해 항상 동일한 잔차의 절대값 합을 가진다. 이로 인해 다양한 함수가 발생할 수 있으므로 곡선 적합에는 부적합하다고 볼 수 있다.

최소-최대 판별

직선으로부터 떨어진 데이터 점들의 최대 거리를 최소화하는 방식이다. 위 그림처럼 전체적인 경향성이 존재하는 상황에서 이런 경향성과 동떨어진 값이 있을 때, 이 특이한 값에 의해 과도하게 영향을 받아 실제 경향을 설명하지 못하는 경우가 존재하므로, 곡선 적합에 이용하기 까다롭다.

잔차의 제곱합을 최소화

위와 같은 이유들로 인해 통계 영역에서도 자주 사용하는 방식이다. 이 방법을 이용하면 주어진 데이터에 대해서 유일한 직선을 도출할 수 있다.

 

직선의 최소제곱 적합

 

위와 같이 Sr이 주어졌을 때 , 이를 최소화하는 a0 및 a1을 구하자.

선형회귀분석 오차의 정량화

 

표준편차 St를 구하는 방식과 추정잔차 제곱의 합 Sr을 구하는 방식이 유사하다.

이때 잔차는 y 좌표에 대해 계산하므로, 직선과의 수직거리가 아니라 y좌표의 차이다.

 

추정값의 표준 오차(Standard error of the estimate)

 

회귀분석 직선에 대한 추정값의 표준오차는 위와 같다.

y/x 는 특정한 값 x에 대응되는 예측값 y의 오차를 의미한다.

데이터를 이용하여 유도되는 a0 및 a1 이 Sr을 계산하는데 사용되어 자유도를 잃는다 (n-2).

 

결정계수(coefficient of determination)

 

  • St : 평균값 주위 제곱의 합
  • Sr : 회귀분석 직선 주위의 잔차 제곱의 합

이때 St - Sr 은 개선된 정도 또는 오차의 감소를 의미한다. 이 값을 정규화하면,

이때 r2결정계수, r상관계수라고 한다.

  • r2 = 1, Sr = 0, 완전 적합
  • r2 = 0, St = Sr, 개선 없음
  • 0 <= r2 <= 1

 

 

최소제곱 선형회귀 분석 알고리즘(파이썬)

from math import sqrt


def Regress(x: list[float], y: list[float]):
    '''
    @param x : x 좌표에 해당하는 배열
    @param y : y 좌표에 해당하는 배열

    @return (a0, a1, r2, syx): 
        a0, a1  : 회귀분석으로 알아낸 식의 값
        r2      : 결정계수
        syx     : 추정값의 표준 오차
    '''
    sumx: float = 0  # x
    sumy: float = 0  # y
    sumxy: float = 0  # xy
    sumx2: float = 0  # x^2
    st: float = 0  # 평균값 주위 제곱 합
    sr: float = 0  # 회귀분석 잔차 제곱 합
    a1: float  # 계수
    a0: float  # 절편

    if len(x) != len(y):
        print("두 배열의 크기가 같지 않습니다.")

    n = len(x)  #원소의 개수

    # 각 값을 구해 a0, a1을 만들기 대기
    for i in range(n):
        sumx += x[i]
        sumy += y[i]
        sumxy += x[i] * y[i]
        sumx2 += x[i]**2

    xm = sumx / n  #평균
    ym = sumy / n

    #계수 구하기
    a1 = (n * sumxy - sumx * sumy) / (n * sumx2 - sumx**2)
    a0 = ym - a1 * xm

    for i in range(n):
        st += (y[i] - ym)**2
        sr += (y[i] - a0 - a1 * x[i])**2

    syx = sqrt(sr / (n - 2))
    r2 = (st - sr) / st

    return (a0, a1, r2, syx)


if __name__ == "__main__":
    x = [1, 2, 3, 4, 5, 6, 7]
    y = [0.5, 2.5, 2.0, 4.0, 3.5, 6.0, 5.5]
    (a0, a1, r2, syx) = Regress(x, y)
    print(f"a0 : {a0:.3f} , a1 : {a1:.3f} , r2 : {r2:.3f} , syx : {syx:.3f}")

 

결과