ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • chapter4 모델 훈련
    공부/Hands-On Machine Learning 2023. 1. 18. 12:42
    728x90

    가장 간단한 선형 회귀부터 시작, 방법은 2가지

    • 직접 계산할 수 있는 공식을 사용하여 훈련 세트에 가장 잘 맞는 모델 파라미터(즉, 훈련세트에 대해 비용함수를 최소화하는 모델 파라미터)를 해석적으로 구함
    • 경사하강법(GS)이라 불리는 반복적인 최적화 방식을 사용하여 모델 파라미터를 조금씩 바꾸면서 비용 함수 훈련 세트에 대해 최소화, 결국에는 앞의 방법과 동일한 파라미터로 수렴함, 경사 하강법의 변종으로 2부에서 신경망 고부할 때 계속 사용하게 될 배치 경사하강법, 미니배치 경사하강법, 확률적 경사 하강법(SGD)도 살펴보겠음

    다음은 다항 회귀를 살펴봄

    이 모델은 선형 회귀보다 파라미터가 많아서 훈련 데이터에 과대적합되기 더 쉬움

    따라서 학습 곡선을 사용해 모델이 과대적합되는 지 감지하는 방법도 살펴보겠음

    그런다음 훈련세트의 과대적합을 감소시킬 수 있는 규제 기법을 몇가지 알아보겠음

     

    마지막으로 분류 작업에 널리 사용하는 모델인 로지스틱 회귀와 소프트맥스 회귀를 살펴봄

    4.1 선형회귀

    1장에서 본 삶의 만족도에 대한 간단한 선형 회귀 모델

    이 모델은 입력 특성인 1인당_GDP에 대한 선형 함수

    셋타들이 모델 파라미터

    더 일반적으로 선형 모델은 입력 특성의 가중치 합과 편향(절편)이라는 상수를 더해 예측을 만듦

    이 식은 벡터 형태로 더 간단하게 쓸 수 있음

    이것이 선형회귀모델

    모델을 훈련시킨다는 것은 모델이 훈련 세트에 가장 잘 맞도록 모델 파라미터를 설정하는 것

    이를 위해 먼저 모델이 훈련 데이터에 얼마나 잘 들어맞는지 측정해야함

    2장에서 회귀에 가장 널리 사용되는 성능 측정 지표는 평균 제곱근 오차(RMSE)라고 언급

    그러므로 선형 회귀 모델을 훈련시키려면 RMSE를 최소화하는 셋타를 찾아야함

    실제로는 RMSE보다 평균제곱오차(MSE)를 최소화하는 것이 같은 결과를 내면서 더 간단

    선형 회귀 모델의 MSE 비용함수

    4.1.1 정규방정식

    비용 함수를 최소화하는 셋타값을 찾기 위한 해석적인 방법이 있음

    다른 말로 하면 바로 결과를 얻을 수 있는 수학 공식이 있음=>정규방정식

    %matplotlib inline
    import matplotlib as mpl
    import matplotlib.pyplot as plt
    mpl.rc('axes', labelsize=14)
    mpl.rc('xtick', labelsize=12)
    mpl.rc('ytick', labelsize=12)
    %matplotlib inline
    import matplotlib as mpl
    import matplotlib.pyplot as plt
    mpl.rc('axes', labelsize=14)
    mpl.rc('xtick', labelsize=12)
    mpl.rc('ytick', labelsize=12)
    
    plt.plot(X, y, "b.")
    plt.xlabel("$x_1$", fontsize=18)
    plt.ylabel("$y$", rotation=0, fontsize=18)
    plt.axis([0, 2, 0, 15])
    plt.show()

     정규방정식을 사용해 셋타를 계산해보겠음

    넘파이 선형대수 모듈(np.linalg)에 있는 inv()함수를 사용해 역행렬을 계산하고 dot()메서드를 사용해 행렬곱셈을 하겠음

    X_b=np.c_[np.ones((100,1)),X]
    theta_best=np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y)
    theta_best​

    이 데이터를 생성하기 위해 사용한 함수는 y=4+3x+가우시안 잡음

    셋타0은 4.559753와 셋타1은 2.97662767 대신 셋타0은 4, 셋타1은 3을 기대했었음

    비슷하지만 자븜 떄문에 원래 함수의 파라미터를 정확하게 재현하지 못했음

    X_new=np.array([[0],[2]])
    X_new_b=np.c_[np.ones((2,1)),X_new]
    y_predict=X_new_b.dot(theta_best)
    y_predict

    plt.plot(X_new,y_predict,'r-')
    plt.plot(X,y,"b.")
    plt.axis([0,2,0,15])
    plt.show()

    from sklearn.linear_model import LinearRegression
    
    lin_reg=LinearRegression()
    lin_reg.fit(X,y)
    lin_reg.intercept_, lin_reg.coef_

    lin_reg.predict(X_new)

    LinearRegression클래스는 scipy.linalg.lstsq()함수를 기반으로 함. 직접호출가능

    theta_best_svd, residuals, rank,s=np.linalg.lstsq(X_b,y,rcond=1e-6)
    theta_best_svd

    이 함수는 셋타햇=(X^+)+y을 계산

    여기서 X^+는 X의 유사역행렬

    np.linalg.pinv()함수를 사용해 유사 역행렬을 직접 구할 수 있음

    np.linalg.pinv(X_b).dot(y)

    유사역행렬 자체는 특잇값 분해(SVD)라 부르는 표준 행렬 분해 기법을 사용해 계산

    SVD는 훈련세트행렬X를 3개의 행렬곱셈 U∑V^T로 분해

    유사 역행렬은 X^+=V*∑^+*U^T로 계산

    ∑^+를 계산하기 위해 알고리즘이 ∑를 먼저 구하고 다음 어떤 낮은 임곗값보다 작은 모든 수를 0으로 바꿈

    그 다음 0이 아닌 모든 값을 역수로 치환

    마지막으로 만들어진 행렬을 전치

    정규방정식을 계산하는 것보다 이 방식이 훨씬 효율적

    극단적인 경우도 처리할 수 있음

    m<n이거나 어떤 특성이 중복되어 행렬X^T*X의 역행렬이 없다면(즉, 특이행렬이라면)정규방정식이 작동하지 않음

    하지만 유사역행렬은 항상 구할 수 있음

    4.1.2 계산 복잡도

    정규방정식 (n+1)(n+1)크기가 되는 X^T*X의 역행렬을 계산

    역행렬을 계산하는 계산복잡도는 일반적으로 O(n^2,4)에ㅔ서 O(n^3)사이

    특성 수가 두 배로 늘어나면 계산 시간이 대략 2^2.3=5.3에서 2^3=8배로 증가

    사이킷런의 LinearRegression클래스가 사용하는 SVD방법은 O(n^2)

    특성의 개수가 두배로 늘어나면 계산 시간이 대략 4배가 됨

    학습된 선형 회귀 모델은 예측이 매우 빠름

    예측 계산 복잡도는 샘플 수와 특성 수에 선형적, 다시 말해 예측하려는 샘플이 두 배로 늘어나면(또는 특성이 두배로 늘어나면)걸리는 시간도 거의 두 배 증가

    이 방법은 특성이 매우 많고 훈련 샘플이 너무 많아 메모리에 모두 담을 수 없을 때 적합

    4.2 경사 하강법

    여러 종류의 문제에서 최적의 해법을 찾을 수 있는 일반적인 최적화 알고리즘

    경사하강법의 기본 아이디어는 비용 함수를 최소화하기 위해 반복해서 파라미터를 조정해가는 것

    파라미터 벡터 셋타에 대해 비용 함수의 현재 그레디언트를 계산

    그리고 그레디언트가 감소하는 방향으로 진행

    그래디언트가 0이 되면 최솟값에 도달!

    구체적으로 보면 셋타를 임의의 값으로 시작해서(무작위 초기화,randonm initialization)한 번에 조금씩 비용함수(ex.MSE)가 감소되는 방향으로 진행하여 알고리즘이 최솟값에 수렴할 때까지 점진적으로 향상

    경사 하강법에서 중요한 파라미터는 스텝의 크기로 학습률 파라미터로 결정됨

    학습률이 너무 작으면 알고리즘이 수렴하기 위해 반복을 많이 진행해야하므로 시간이 오래 걸림

    학습률이 너무 크면 골짜기를 가로질러 반대편으로 건너뛰게 되어 이전보다 더 높은 곳으로 올라가게 될지도 모름

    이는 알고리즘을 더 큰 값으로 발산하게 만들어 적절한 해법을 찾지 못하게 함

    모든 비용 함수가 매끈한 그릇과 같지는 않음

    아래 그림은 두가지 문제점을 보여줌

    무작위 초기화 때문에 알고리즘이 왼쪽에서 시작하면 전역 최솟값보다 덜 좋은 지역 최솟값에 수렴

    알고리즘이 오른쪽에서 시작하면 평탄한 지역을 지나기 위해 시간이 오래 걸리고 일찍 멈추게 되어 전역 최솟값에 도달하지 못함

    다행히 선형 회귀를 위한 MSE 비용 함수는 곡선에서 어떤 두 점을 선택해 선을 그어도 곡선을 가로지르지 않는 볼록함수

    이는 지역 회솟값이 없고 하난의 전역 최솟값만 있다는 뜻

    또한 연속된 함수이고 기울기가 갑자기 변하지 않음

    이 두 사실로부터 경사 하강법이 전역 최솟값에 가깝게 접근할 수 있다는 것을 보장

     

    사실 비용함수는 그릇 모양을 하고 있지만 특성들의 스케일이 매우 다르면 길쭉한 모양일 수 있음

    특성 1과 특성 2의 스케일이 같은 훈련세트(왼쪽 아래 그림), 특성 1이 특성2 보다 더 작은 훈련세트(오른쪽 아래그림)에 대한 경사 하강법을 보여줌

    왼쪽의 경사 하강법 알고리즘이 최솟값으로 곧장 진행하고 있어 빠르게 도달

    반면 오른쪽은 처음엔 전역 최솟값의 방향에 거의 직각으로 향하다가 평편한 골짜기를 길게 돌아서 나감

    결국 최솟값에 도달하겠지만 시간이 오래 걸릴 것

    더보기

    경사 하강법을 사용할 때는 반드시 모든 특성이 같은 스케일을 갖도록 만들어야 함

    예를 들어 사이킷런의 StandardScaler를 사용

    그렇지 않으면 수렴하는데 훨씬 오래 걸림

    앞의 그림은 모델 훈련이 비용 함수를 최소화하는 모델 파라미터의 조합을 찾는 일

    이를 모델의 파라미터 공간에서 찾는다고 말함

    모델이 가진 파라미터가 많을수록 이 공간의 차원은 커지고 검색이 더 어려워짐

    다행히 선형 회귀의 경우 비용함수가 볼록함수이기때문에 최솟값은 그래프의 맨 아래에 있을 것

    4.2.1 배치 경사 하강법

    경사 하강법을 구현하려면 각 모델 파라미터에 대해 비용 함수의 그레디언트를 계산해야함

    다시 말해 모델 파라미터가 조금 변경될 때 비용 함수가 얼마나 바뀌는지 계산해야함

    이를 편도함수(partial derivative)라고 함

    이는 '동쪽을 바라봤을 떄 발밑에 느껴지는 산의 기울기는 얼마인가?'와 같은 질문

    같은 질문을 북쪽에 대해서도 함(3차원 이상의 세상이라 가정하면 다른 모든 차우너에 대해서 반복)

    파라미터에 대한 비용함수의 편도함수 MSE(셋타)임

    편도함수를 각각 계산하는 대신 다음의 식을 사용하여 한꺼번에 계산할 수 있음

    그래디언트 벡터 ▽셋타MSE(셋타)는 비용함수의 모델 파라미터마다 한개씩인 편도함수를 모두 담고 있음

    더보기

    이 공식은 경사하강법 스텝에서 전체 훈련 세트 X에 대해 계산

    그래서 이 알고리즘을 배치경사하강법이라고 함

    즉 , 매 스텝에서 훈련 데이터 전체를 사용

    이런 이유로 매우 큰 훈련 세트에서는 아주 느림

    그러나 경사 하강법은 특성 수에 민감하지 않음

    수십만 개의 특성에서 선형 회귀를 훈련시키려면 정규방정식이나 SVD분해보다 경사 하강법을 사용하는 편이 훨씬 빠름

    위로 향하는 그래디언트 벡터가 구해지면 반대 방향인 아래로 가야함

    위 알고리즘을 간단히 구현

    eta=0.1 #학습률
    n_iterations = 1000
    m=100
    
    theta=np.random.randn(2,1) #무작위 초기화
    
    for iteration in range(n_iterations):
      gradients=2/m*X_b.T.dot(X_b.dot(theta)-y)
      theta=theta-eta*gradients
      
    theta

    정규방정식으로 찾은 것과 같음

    경사 하강법이 완벽하게 작동했음

    학습률을 바꿔보면 어떨까??

    아래 그림은 세가지 다른 학습률을 사용하여 진행한 경사 하강법의 스템 처음 10개를 보여줌

    def plot_gradient_descent(theta, eta, theta_path=None):
        m = len(X_b)
        plt.plot(X, y, "b.")
        n_iterations = 1000
        for iteration in range(n_iterations):
            if iteration < 10:
                y_predict = X_new_b.dot(theta)
                style = "b-" if iteration > 0 else "r--"
                plt.plot(X_new, y_predict, style)
            gradients = 2/m * X_b.T.dot(X_b.dot(theta) - y)
            theta = theta - eta * gradients
            if theta_path is not None:
                theta_path.append(theta)
        plt.xlabel("$x_1$", fontsize=18)
        plt.axis([0, 2, 0, 15])
        plt.title(r"$\eta = {}$".format(eta), fontsize=16)
    np.random.seed(42)
    theta = np.random.randn(2,1)  # random initialization
    
    plt.figure(figsize=(10,4))
    plt.subplot(131); plot_gradient_descent(theta, eta=0.02)
    plt.ylabel("$y$", rotation=0, fontsize=18)
    plt.subplot(132); plot_gradient_descent(theta, eta=0.1, theta_path=theta_path_bgd)
    plt.subplot(133); plot_gradient_descent(theta, eta=0.5)
    
    plt.show()

    왼쪽은 학습률이 너무 낮아 알고리즘은 최적점에 도달은 하겠지만 시간이 오래 걸릴 것

    가운데는 학습률이 아주 적당해 보임

    오른쪽은 학습률이 너무 높아 알고리즘이 이리저리 널뛰면서 스텝마다 최적점에서 점점 더 멀어져 발산

    =>적절한 학습률을 찾으려면 그리드 탐색을 사용

    하지만 그리드 탐색에서 수렴하는데 너무 오래 걸리는 모델을 막기 위해 반복 횟수 제한해야함

    반복횟수의 지정방법

    너무 작으면 최적점에 도달하기 전에 알고리즘이 멈추고 너무 크면 모델 파라미터가 더는 변하지 않는 동안 시간낭비

    간단한 해결책은 반복횟수를 아주 크게 지정하고 그래디언트 벡터가 아주 작아지면, 즉 벡터의 오름이 어떤 값 허용오차보다 작아지면 경사 하강법이 최솟값에 도달한 것이므로 알고리즘 정지

    4.2.2 확률적 경사 하강법

    배치 경사 하강법의 가장 큰 문제는 매 스텝에서 전체 훈련 세트를 사용해 그레디어트를 계산한다는 사실

    훈련세트가 컺지면 매우 느려지게 됨

    이와는 반대로 확률적 경사 하강법은 매 스텝에서 한개의 샘플을 무작위로 선택하고 그 하나의 샘플에 대한 그레디언트를 계산

    매 반복에서 다뤄야 할 데이터가 매우 적기 떄문에 한번에 하나의 샘플을 처리하면 알고리즘이 훨씬 빠름

    또한 매 반복에서 하나의 샘플만 메모리에 있으면 되므로 매우 큰 훈련 세트도 훈련 가능

    반면 확률적이므로 이 알고리즘은 배치경사하강법보다 훨씬 불안정

    비용 함수가 최솟값에 다다를때까지 부드럽게 감소하지 않고 위아래로 요동치며 평균적으로 감소

    시간이 지나면 최솟값에 매우 근접하겠지만 요동이 지속되면서 최솟값에 안착하지 못할 것

    알고리즘이 멈출 때 좋은 파라미터가 구해지겠지만 최적치는 아님

    비용 함수가 매우 불규칙할 때 알고리즘이 지역 최솟값을 건너뛰도록 도와주므로 확률적 경사 하강법이 배치 경사 하강법보다 전역 최솟값을 찾을 가능성이 높음

    무작위성ㅇ은 지역 최솟값에서 탈출시켜줘서 좋지만 알고리즘을 전역 최솟값에 다다르지 못하게 한다는 점에서 나쁨

    이 딜레마를 해결하는 한 가지 방법은 학습률을 점진적으로 감소시키는 것

    시작할 때는 학습률을 크게 하고(수렴을 빠르게 하고 지역 최솟값에 빠지지 않게 함) 점차 작게 줄여서 알고리즘이 전역 최솟값에 도달하게 함

    이 과정은 금속공학 분야에서 가열한 금속을 천천히 냉각시키는 어닐링과정에서 영감을 받은 담금질 기법 알고리즘과 유사

    매 반복에서 학습률을 결정하는 함수를 학습 스케줄이라고 부름

    학습률이 너무 빨리 줄어들면 지역 최솟값에 갇히거나 최솟값까지 가는 중간에 멈춰버릴수도있음

    학습률이 너무 천천히 줄어들면 오랫동안 최소값 주변을 맴돌거나 훈련을 너무 일찍 중지해서 지역 최솟값에 머무를 수 있음

    n_epochs=50
    t0,t1=5,50  #학습스케줄 파라미터
    
    def learning_schedule(t):
      return t0/(t+t1)
    
    theta=np.random.randn(2,1) #무작위 초기화
    
    for epoch in range(n_epochs):
      for i in range(m):		#m은 훈련 세트에 있는 샘플 수
        random_index=np.random.randint(m)
        xi=X_b[random_index:random_index+1]
        yi=y[random_index:random_index+1]
        gradients=2*xi.T.dot(xi.dot(theta)-yi)
        eta=learning_schedule(epoch*m-1)
        theta=theta-eta*gradients
    
    theta

    일반적으로 한 반복에서 m번 되풀이되고 이 때 각 반복을 에포크라고 함

    배치 경사 하강법 코드가 전체 훈련 세트에 대해 1000번 반복하는 동안 이 코드는 훈련 세트에서 50번만 반복하고도 매우 좋은 값에 도달

    theta_path_sgd = []
    m = len(X_b)
    np.random.seed(42)
    n_epochs = 50
    t0, t1 = 5, 50  # 학습 스케줄 하이퍼파라미터
    
    def learning_schedule(t):
        return t0 / (t + t1)
    
    theta = np.random.randn(2,1)  # 랜덤 초기화
    
    for epoch in range(n_epochs):
        for i in range(m):
            if epoch == 0 and i < 20:                    # 책에는 없음
                y_predict = X_new_b.dot(theta)           # 책에는 없음
                style = "b-" if i > 0 else "r--"         # 책에는 없음
                plt.plot(X_new, y_predict, style)        # 책에는 없음
            random_index = np.random.randint(m)
            xi = X_b[random_index:random_index+1]
            yi = y[random_index:random_index+1]
            gradients = 2 * xi.T.dot(xi.dot(theta) - yi)
            eta = learning_schedule(epoch * m + i)
            theta = theta - eta * gradients
            theta_path_sgd.append(theta)                 # 책에는 없음
    
    plt.plot(X, y, "b.")                                 # 책에는 없음
    plt.xlabel("$x_1$", fontsize=18)                     # 책에는 없음
    plt.ylabel("$y$", rotation=0, fontsize=18)           # 책에는 없음
    plt.axis([0, 2, 0, 15])                              # 책에는 없음
    plt.show()                                           # 책에는 없음

    샘플을 무작위로 선택하기 떄문에 어떤 샘플은 한 에포크에서 여러번 선택 될수도 있고 어떤 샘플은 전혀 선택되지 못할 수도 있음

    알고리즘이 에포크마다 모든 샘플을 사용하게 하려면 훈련 세트를 섞은 후(입력특성과 레이블을 동일하게 섞어야함) 차례대로 하나씩 선택하고 다음 에포크에서 다시 섞는 식의 방법을 사용할 수 있음

    그러나 이렇게 하면 보통 더 늦게 수렴됨

    더보기

    확률적 경사 하강법을 사용할 때 훈련 샘플이 IID(Independent and Identically distributed)를 만족해야 평균적으로 파라미터가 전역 최적점을 향해 진행한다고 보장

    이렇게 만드는 간단한 방법은 훈련하는 동안 샘플을 섞는 것

    만약 레이블 순서대로 정렬된 샘플처럼 샘플을 섞지 않은채로 사용하면 확률적 경사 하강법이 먼저 한 레이블에 최적화하고 그 다음 두번째 레이블을 회적화하는 식으로 진행됨

    결국 이 모델은 최적점에 가깝게 도달하지 못할 것

    사이킷런에서 SGD방식으로 선형회귀를 사용하려면 기본값으로 제곱오차비용함수를 최적화하는 SGDRegressor클래스를 사용

    다음 코드는 최대 1000번 에포크동안 실행(max_iter=1000)

    또는 한 에포크에서 0.001보다 적게 손실이 줄어들 때까지 실행(tol=1e-3)

    학습률 0.1(eta0=0.1)로 기본 학습 스케줄을 사용

    규제는 전혀 사용하지 않았음(penalty=None)

    from sklearn.linear_model import SGDRegressor
    
    sgd_reg=SGDRegressor(max_iter=1000,tol=1e-3,penalty=None, eta0=0.1)
    sgd_reg.fit(X, y.ravel())
    
    sgd_reg.intercept_, sgd_reg.coef_

    정규방정식으로 구한 것과 매우 비슷한 값을 얻음

    4.2.3 미니배치 경사 하강법

    각 스텝에서 전체 훈련 세트나 하나의 샘플을 기반으로 그레디언트를 계산하는 것이 아니라 미니배치라 부르는 임의의 작은 샘플 세트에 대해 그레디언트를 계산

    확률적 경사 하강법에 비해 미니배치 경사 하강법의 주요 장점은 행렬 연산에 최적화된 하드웨어, 특히 GPU를 사용해서 얻는 성능 향상

    특히 미니배치를 어느 정도 크게하면 이 알고리즘은 파라미터 공간에서 SGD보다 덜 불규칙하게 움직임

    미니배치 경사 하강법이 SGD보다 최솟값에 더 가까이 도달하게 될 것

    하지만 지역최솟값에서 빠져나오기는 더 힘들지도 모름(선형회귀와 같지 않고 지역 최솟값이 문제가 되는 경우)

    theta_path_mgd = []
    
    n_iterations = 50
    minibatch_size = 20
    
    np.random.seed(42)
    theta = np.random.randn(2,1)  # 랜덤 초기화
    
    t0, t1 = 200, 1000
    def learning_schedule(t):
        return t0 / (t + t1)
    
    t = 0
    for epoch in range(n_iterations):
        shuffled_indices = np.random.permutation(m)
        X_b_shuffled = X_b[shuffled_indices]
        y_shuffled = y[shuffled_indices]
        for i in range(0, m, minibatch_size):
            t += 1
            xi = X_b_shuffled[i:i+minibatch_size]
            yi = y_shuffled[i:i+minibatch_size]
            gradients = 2/minibatch_size * xi.T.dot(xi.dot(theta) - yi)
            eta = learning_schedule(t)
            theta = theta - eta * gradients
            theta_path_mgd.append(theta)
    
    theta

    세가지 경사 하강법 알고리즘이 훈련과정동안 파라미터 공간에서 움직인 경로

    theta_path_bgd = np.array(theta_path_bgd)
    theta_path_sgd = np.array(theta_path_sgd)
    theta_path_mgd = np.array(theta_path_mgd)
    
    plt.figure(figsize=(7,4))
    plt.plot(theta_path_sgd[:, 0], theta_path_sgd[:, 1], "r-s", linewidth=1, label="Stochastic")
    plt.plot(theta_path_mgd[:, 0], theta_path_mgd[:, 1], "g-+", linewidth=2, label="Mini-batch")
    plt.plot(theta_path_bgd[:, 0], theta_path_bgd[:, 1], "b-o", linewidth=3, label="Batch")
    plt.legend(loc="upper left", fontsize=16)
    plt.xlabel(r"$\theta_0$", fontsize=20)
    plt.ylabel(r"$\theta_1$   ", fontsize=20, rotation=0)
    plt.axis([2.5, 4.5, 2.3, 3.9])
    plt.show()

    모두 최솟값 근처에 도달했지만 배치 경사 하강법의 경로가 실제로 최솟값에서 멈춘 반면 확률적 경사하강법과 미니배치 경사하강법은 근처에서 맴돌고 있음

    하지만 배치 경사 하강법에는 스텝에서 많은 시간이 소요되고 확률적 경사 하강법과 미니배치 경사 하강법도 적절한 학습 스케줄을 사용하면 최솟값에 도달한다는 것을 잊지 말아야함

    4.3 다항회귀

    비선형 데이터를 학습하는데 선형 모델을 사용할 수 있음

    이렇게 하는 간단한 방법은 각 특성의 거듭제곱을 새로운 특성으로 추가하고, 이 확장된 특성을 포함한 데이터셋에 선형 모델을 훈련시키는 것->다항 회귀

    간단한 2차방정식으로 비선형 데이터를 생성하겠음(약간의 잡음 포함)

    m=100
    X=6*np.random.rand(m,1)-3
    y=0.5*X**2+X+2+np.random.randn(m,1)
    plt.plot(X, y, "b.")
    plt.xlabel("$x_1$", fontsize=18)
    plt.ylabel("$y$", rotation=0, fontsize=18)
    plt.axis([-3, 3, 0, 10])
    plt.show()

    확실히 직선은 이 데이터에 잘 맞지 않을 것

    사이킷런의 PolynomialFeatures를 사용해 훈련 데이터를 변환

    훈련 세트에 있는 각 특성을 제곱(2차 다항)하여 새로운 특성으로 추가함(여기서는 특성 하나가 추가됨)

    from sklearn.preprocessing import PolynomialFeatures
    
    poly_features=PolynomialFeatures(degree=2,include_bias=False)
    X_poly=poly_features.fit_transform(X)
    print(X[0],X_poly[0])

    X_poly는 이제 원래 특성 X와 이 특성의 제곱을 포함

    이 확장된 훈련 데이터에 Linear Regression을 적용

    lin_reg=LinearRegression()
    lin_reg.fit(X_poly,y)
    lin_reg.intercept_,lin_reg.coef_

    X_new=np.linspace(-3, 3, 100).reshape(100, 1)
    X_new_poly = poly_features.transform(X_new)
    y_new = lin_reg.predict(X_new_poly)
    plt.plot(X, y, "b.")
    plt.plot(X_new, y_new, "r-", linewidth=2, label="Predictions")
    plt.xlabel("$x_1$", fontsize=18)
    plt.ylabel("$y$", rotation=0, fontsize=18)
    plt.legend(loc="upper left", fontsize=14)
    plt.axis([-3, 3, 0, 10])
    plt.show()

    낫 밷, 실제 원래 함수가 y=0.5x^2+i.0x+2.0+가우시안 잡음이고 예측된 모델은 y=0.53^2+0.95x+1.97임

    특성이 여러개일때 다항 회귀는 이 특성 사이의 관계를 찾을 수 있음

    PolynomialFeatures가 주어진 차수까지 특성 간의 모든 교차항을 추가하기 때문

    예를 들어 두개의 특성 a,b가 있을 때 degree=3으로 PolynomialFeatures를 적용하면 a^3,a^2,b^3,b^2뿐만아니라 ab,a^2b,ab^2도 특성으로 추가함

    더보기

    PolynomialFeaturees(degree=d)은 특성이 n개인 배열을 특성이 (n+d)!/n!d!개인 배열로 변환

    여기서 n!은 n의 팩토리얼로 1*2*3*...*n과 같음

    특성수가 교차항을 포함해 엄청나게 늘어날 수 있으니 주의해야함

    4.4 학습곡선

    고차다항회귀를 적용하면 보통의 선형 회귀에서보다 훨씬 더 훈련 데이터에 잘 맞추려할 것

    예를 들어 300차 다항 회귀모델을 이전의 훈련 데이터에 적용한 것 단순한 선형 모델이나 2차모델과 결과를 비교

    300차항 회귀 모델은 훈련 샘플에 가능한 한 가까이 가려고 구불구불하게 나타남

    이 고차 다항 회귀 모델은 심각하게 훈련 데이터에 과대 적합

    반면에 선형 모델은 과소 적합

    이 경우 가장 일반화가 잘된 모델은 2차 다항 회귀

    2차 방정식으로 생성한 데이터이기때문에 당연한 결과지만 일반적으로는 어떤 함수로 데이터가 생성됐는지 알 수 없음

    그러면 얼마나 복잡한 모델을 사용할지 어떻게 결정할수있을까?어떻게 모델이 데이터에 과대적합/과소적합됐는지 알수있을까?

     

    2장에서 모델의 일반화 성능을 추정하기 위해 교차 검증을 사요했음

    훈련데이터에서 성능이 좋지만 교차 검증 점수가 나쁘다면 모델이 과대적합된 것

    만약 양쪽에 모두 좋지 않으면 과소 적합

    이 때 모델이 너무 단순하거나 너무 복잡하다고 말함

     

    또 다른 방법은 학습 곡선을 살펴보는 것

    이 그래프는 훈련 세트와 검증 세트의 모델 성능을 훈련 세트 크기(또는 훈련 반복)의 함수로 나타냄

    이 그래프를 생성하기 위해서는 단순히 훈련 세트에서 크기가 다른 서브 세트를 만들어 모델을 여러번 훈련시키면 됨

    다음 코드는 주어진 훈련 데이터에서 모델의 학습곡선을 그리는 함수를 정의

    from sklearn.metrics import mean_squared_error
    from sklearn.model_selection import train_test_split
    
    def plot_learning_curves(model,X,y):
      X_train,X_val, y_train,y_val=train_test_split(X,y,test_size=0.2)
      train_errors,val_errors=[],[]
      for m in range(1, len(X_train)):
        model.fit(X_train[:m],y_train[:m])
        y_train_predict=model.predict(X_train[:m])
        y_val_predict=model.predict(X_val)
        train_errors.append(mean_squared_error(y_train[:m],y_train_predict))
        val_errors.append(mean_squared_error(y_val,y_val_predict))
      plt.plot(np.sqrt(train_errors),'r-+',linewidth=2,label="train set")
      plt.plot(np.sqrt(val_errors),'b-',linewidth=2,label="val set")
      plt.legend(loc="upper right", fontsize=14)
    lin_reg = LinearRegression()
    plot_learning_curves(lin_reg, X, y)
    plt.axis([0, 80, 0, 3])                         
    plt.show()

    과소적합된 이 모델은 설명이 필요함

    먼저 훈련 데이터의 성능을 보겠음

    그래프가 0에서 시작하므로 훈련세트에 하나 혹은 두 개의 샘플이 있을 땐 모델이 완벽하게 작동

    하지만 훈련 세트에 샘플이 추가됨에 따라 잡음도 있고 비선형이기 때문에 모델이 훈련 데이터를 환벽히 학습하는 것이 불가능해짐

    그래서 곡선이 어느정도 평편해질떄까지 오차가 계속 상승

    이 위치에서는 훈련 세트에 샘플이 추가되어도 평균 오차가 크게 나아지거나 나빠지지 않음

    그럼 이제 검증 데이터에 대한 모델의 성능을 보겠음

    모델이 적은 수의 훈련 샘플로 훈련될 때는 제대로 일반화될 수 없어서 검증 오차가 초기에 매우 큼

    모델에 훈련 샘플이 추가됨에 따라 하긋ㅂ이 되고 검증 오차가 천천히 감소

    하지만 선형 회귀의 직선은 데이터를 잘 모델링할 수 없으므로 오차의 감소가 완만해져 훈련 세트의 그래프와 가까워짐

    이 학습곡선이 과소적합 모델의 전형적인 모습

    두 곡선이 수평한 구간을 만들고 꽤 높은 오차에서 매우 가까이 근접해 있음

     

    이제 같은 데이터에서 10차 다항 회귀 모델의 학습곡선을 그려보겠음

    from sklearn.pipeline import Pipeline
    
    polynomial_regression = Pipeline([
        ('poly_features',PolynomialFeatures(degree=10,include_bias=False)),
        ("lin_reg",LinearRegression())
    ])
    plot_learning_curves(polynomial_regression,X,y)

    두 학습곡선의 차이점 

    • 위의 그래프는 훈련 데이터와 오차가 선형 회귀 모델보다 훨씬 낮음
    • 두 곡선 사이에 공간이 있음, 이 말은 훈련데이터에서의 모델 성능이 검증 데이터에서보다 훨씬 낫다는 뜻, 이는 과대적합 모델의 특징 그러나 더 큰 훈련 세트를 사용하면 두 곡선이 점점 가까워짐
    더보기

    편향/분산 트레이드오프

    통계학과 머신러닝에서 나온 중요한 이론 하나는 모델의 일반화 오차는 세가지 다른 종류의 오차의 합으로 표현할 수 있다는 점

    1. 편향
      • 일반화 오차 중에서 편향은 잘못된 가정으로 인한 것
      • 예를 들어 데이터가 실제로는 2차인데 선형으로 가정하는 경우
      • 편향이 큰 모델은 훈련 데이터에 과소적합되기 쉬움
    2. 분산
      • 분산은 훈련 데이터에 있는 작은 변동에 모델이 과도하게 민감하기 떄문에 나타남
      • 자유도가 높은 모델(ex.고차 다항회귀모델)이 높은 분산을 가지기 쉬워 훈련 데이터에 과대적합되는 경향
    3. 줄일 수 없는 오차
      • 줄일 수 없는 오차는 데이터 자체에 있는 잡음 때문에 발생
      • 이 오차를 줄일 수 있는 유일한 방법은 데이터에서 잡음을 제거하는 것

    모델의 복잡도가 커지면 통상적으로 분산이 늘어나고 편향은 줄어듦

    반대로 모델의 복잡도가 줄어들면 편향이 커지고 분산이 작아짐

    4.5 규제가 있는 선형 모델

    과대적합을 줄이는좋은 방법은 모델을 규제하는 것(즉, 모델을 제한)

    자유도를 줄이면 데이터에 과대적합되기 더 어려워짐

    다항 회귀 모델을 규제하는 간단한 방법은 다항식의 차수를 감소시키는 것

    선형 회귀 모델에서는 보통 모델의 가중치를 제한함으로써 규제를 가함 

    각기 다른 방법으로 가중치를 제한하는 '릿지'회쉬,'라쏘' 회귀, 엘라스틱넷을 살펴보겠음

    4.5.1 릿지 회귀

    릿지 회귀는 규제가 추가된 선형 회귀 버전, 규제항이 비용함수에 추가

    학습 알고리즘을 데이터에 맞추는 것뿐만 아니라 모뎀의 가중치가 가능한 한 작게 유지되도록 노력

    규제항은 훈련하는 동안에만 비용 함수에 추가

    모델의 훈련이 끝나면 모델의 성능을 규제가 없는 성능 지표로 평가

    더보기

    일반적으로 훈련하는 동안 사용되는 비용 함수와 테스트에서 사용되는 성능 지표는 다름

    규제를 떠나 이들이 다른 이유는 훈련에 사용되는 비용 함수는 최적화를 위해 미분 가능해야하기 떄문

    반면 테스트에 사용되는 성능 지표는 최종 목표에 가능한 한 가까워야 함

    로그 손실같은 비용함수를 사용하여 훈련시킨 분류기를 정밀도/재현율을 사용하여 평가하는 것이 좋은 예

    하이퍼파라미터 알파는 모델을 얼마나 많이 규제할지 조절

    알파가 0이라면 릿지 회귀는 선형 회귀와 같아짐

    알파가 아주 크면 모든 가중치가 거의 0에 가까워지고 결국 데이터의 평균을 지나는 수평선이 됨

    from sklearn.linear_model import Ridge
    ridge_reg = Ridge(alpha=1, solver="cholesky", random_state=42)
    ridge_reg.fit(X, y)
    ridge_reg.predict([[1.5]])
    ridge_reg = Ridge(alpha=1, solver="sag", random_state=42)
    ridge_reg.fit(X, y)
    ridge_reg.predict([[1.5]])
    from sklearn.linear_model import Ridge
    
    def plot_model(model_class, polynomial, alphas, **model_kargs):
        for alpha, style in zip(alphas, ("b-", "g--", "r:")):
            model = model_class(alpha, **model_kargs) if alpha > 0 else LinearRegression()
            if polynomial:
                model = Pipeline([
                        ("poly_features", PolynomialFeatures(degree=10, include_bias=False)),
                        ("std_scaler", StandardScaler()),
                        ("regul_reg", model),
                    ])
            model.fit(X, y)
            y_new_regul = model.predict(X_new)
            lw = 2 if alpha > 0 else 1
            plt.plot(X_new, y_new_regul, style, linewidth=lw, label=r"$\alpha = {}$".format(alpha))
        plt.plot(X, y, "b.", linewidth=3)
        plt.legend(loc="upper left", fontsize=15)
        plt.xlabel("$x_1$", fontsize=18)
        plt.axis([0, 3, 0, 4])
    
    plt.figure(figsize=(8,4))
    plt.subplot(121)
    plot_model(Ridge, polynomial=False, alphas=(0, 10, 100), random_state=42)
    plt.ylabel("$y$", rotation=0, fontsize=18)
    plt.subplot(122)
    plot_model(Ridge, polynomial=True, alphas=(0, 10**-5, 1), random_state=42)
    
    plt.show()

    다양한 수준의 릿지 규제를 사용한 선형회귀(왼)와 다항회귀(오)

    왼쪽그는 평범한 릿지 모델을 사용해 선형적인 예측을 만듦

    오른쪽 그래프는 PolynomialFeatures(degree=10)을 사용하여 먼저 데이터를 확장하고 StandardScaler를 사용해 스케일을 조정한 후 릿지 모델 적용

    결국 이는 릿지 규제를 사용한 다항 회귀가 됨

    알파를 증가시킬수록 직선에 가까워지는 것을 볼 수있음

    ->모델의 분산은 줄지만 편향은 커지게 됨

     

    선형 회귀와 마찬가지로 릿지 회귀를 계산하기위해 정규방정식을 사용할 수도 있고 경사 하강법을 사용할 수도 있음

    장단점은 이전과 같음

    릿지 회귀의 정규방정식

     

    다음은 사이킷런에서 정규방정식을 사용한 릿지 회를 적용하는 예

    from sklearn.linear_model import Ridge
    ridge_reg=Ridge(alpha=1,solver='cholesky')
    ridge_reg.fit(X,y)
    ridge_reg.predict([[1.5]])

    다음은 확률적 경사 하강법을 사용했을 때

    sgd_reg=SGDRegressor(penalty="l2")
    sgd_reg.fit(X,y.ravel())
    sgd_reg.predict([[1.5]])

    penalty 매개변수는 사용할 규제를 지정

    l2는 SGD가 비용함수에 가중치 벡터의 l2노름의 제곱을 2로 나눈 규제항을 추가하게 만듦

    =>릿지 회귀와 같음

    4.5.2 라쏘 회귀

    선형회귀의 또 다른 규제된 버전

    릿지 회귀처럼 비용 함수에 규제항을 더하지만 l2노름의 제곱을 2로 나눈것 대신 가중치 벡터의 l1노름을 사용함 

    라쏘 회귀의 비용함수

    from sklearn.linear_model import Lasso
    
    plt.figure(figsize=(8,4))
    plt.subplot(121)
    plot_model(Lasso, polynomial=False, alphas=(0, 0.1, 1), random_state=42)
    plt.ylabel("$y$", rotation=0, fontsize=18)
    plt.subplot(122)
    plot_model(Lasso, polynomial=True, alphas=(0, 10**-7, 1), random_state=42)
    
    save_fig("lasso_regression_plot")
    plt.show()

    다양한 수준의 라쏘 규제를 사용한 선형회귀(왼)와 다항회귀(오)

    라쏘 회귀의 중요한 특징은 덜 중요한 특성의 가중치를 제거하려고 한다는 점(즉, 가중치가 0이 됨)

    예를 들어 위의 그림의 오른쪽 그래프에서 점선(알파-10-7)은 3차 방정식처럼 보임

    차수가 높은 다항 특성이 가중치가 모두 0이 되었음

    다시 말해 라쏘 회귀는 자동으로 특성 선택을 하고 희소 모델을 만듦(즉, 0이아닌  특성의 가중치가 적음)

    %matplotlib inline
    import matplotlib.pyplot as plt
    import numpy as np
    
    t1a, t1b, t2a, t2b = -1, 3, -1.5, 1.5
    
    t1s = np.linspace(t1a, t1b, 500)
    t2s = np.linspace(t2a, t2b, 500)
    t1, t2 = np.meshgrid(t1s, t2s)
    T = np.c_[t1.ravel(), t2.ravel()]
    Xr = np.array([[1, 1], [1, -1], [1, 0.5]])
    yr = 2 * Xr[:, :1] + 0.5 * Xr[:, 1:]
    
    J = (1/len(Xr) * np.sum((T.dot(Xr.T) - yr.T)**2, axis=1)).reshape(t1.shape)
    
    N1 = np.linalg.norm(T, ord=1, axis=1).reshape(t1.shape)
    N2 = np.linalg.norm(T, ord=2, axis=1).reshape(t1.shape)
    
    t_min_idx = np.unravel_index(np.argmin(J), J.shape)
    t1_min, t2_min = t1[t_min_idx], t2[t_min_idx]
    
    t_init = np.array([[0.25], [-1]])
    def bgd_path(theta, X, y, l1, l2, core = 1, eta = 0.05, n_iterations = 200):
        path = [theta]
        for iteration in range(n_iterations):
            gradients = core * 2/len(X) * X.T.dot(X.dot(theta) - y) + l1 * np.sign(theta) + l2 * theta
            theta = theta - eta * gradients
            path.append(theta)
        return np.array(path)
    
    fig, axes = plt.subplots(2, 2, sharex=True, sharey=True, figsize=(10.1, 8))
    for i, N, l1, l2, title in ((0, N1, 2., 0, "Lasso"), (1, N2, 0,  2., "Ridge")):
        JR = J + l1 * N1 + l2 * 0.5 * N2**2
        
        tr_min_idx = np.unravel_index(np.argmin(JR), JR.shape)
        t1r_min, t2r_min = t1[tr_min_idx], t2[tr_min_idx]
    
        levelsJ=(np.exp(np.linspace(0, 1, 20)) - 1) * (np.max(J) - np.min(J)) + np.min(J)
        levelsJR=(np.exp(np.linspace(0, 1, 20)) - 1) * (np.max(JR) - np.min(JR)) + np.min(JR)
        levelsN=np.linspace(0, np.max(N), 10)
        
        path_J = bgd_path(t_init, Xr, yr, l1=0, l2=0)
        path_JR = bgd_path(t_init, Xr, yr, l1, l2)
        path_N = bgd_path(np.array([[2.0], [0.5]]), Xr, yr, np.sign(l1)/3, np.sign(l2), core=0)
    
        ax = axes[i, 0]
        ax.grid(True)
        ax.axhline(y=0, color='k')
        ax.axvline(x=0, color='k')
        ax.contourf(t1, t2, N / 2., levels=levelsN)
        ax.plot(path_N[:, 0], path_N[:, 1], "y--")
        ax.plot(0, 0, "ys")
        ax.plot(t1_min, t2_min, "ys")
        ax.set_title(r"$\ell_{}$ penalty".format(i + 1), fontsize=16)
        ax.axis([t1a, t1b, t2a, t2b])
        if i == 1:
            ax.set_xlabel(r"$\theta_1$", fontsize=16)
        ax.set_ylabel(r"$\theta_2$", fontsize=16, rotation=0)
    
        ax = axes[i, 1]
        ax.grid(True)
        ax.axhline(y=0, color='k')
        ax.axvline(x=0, color='k')
        ax.contourf(t1, t2, JR, levels=levelsJR, alpha=0.9)
        ax.plot(path_JR[:, 0], path_JR[:, 1], "w-o")
        ax.plot(path_N[:, 0], path_N[:, 1], "y--")
        ax.plot(0, 0, "ys")
        ax.plot(t1_min, t2_min, "ys")
        ax.plot(t1r_min, t2r_min, "rs")
        ax.set_title(title, fontsize=16)
        ax.axis([t1a, t1b, t2a, t2b])
        if i == 1:
            ax.set_xlabel(r"$\theta_1$", fontsize=16)
    
    plt.show()

    라쏘 대 릿지 규제

    위의 그림을 보면 쉽게 이해할 수 있음

    두축은 모델 파라미터 두 개를 나타내고 배경의 등고선은 각기 다른 손실함수를 나타냄

     

    왼쪽 위 그래프의 등고선은 l1손실을 나타냄

    축에 가까워지면서 선형적으로 줄어듦

    예를 들어 모델 파라미터를 셋타1=2셋타2=0.5로 초기화하고 경사하강법을 실행하면 두 파라미터가 동일하게 감소될 것(노란 점선)

    따라서 셋타2가 먼저 0에 도달함(셋타2가 시작할 때 0에 더 가깝기 떄문)

    그다음 경사 하강법이 셋타1=0에 도달할 때까지 축을 따라 내려감(l1의 그레디언트는 0에서 정의되지 않기 떄문에 동이 있음, 이 지점에서 그레디언트는 -1또는 1임)

     

    오른쪽 위 그래프의 등고선은 라쏘 손실 함수를 나타냄(즉 l1손실을 더한 MSE손실함수)
    하얀 작은 원이 경사 하강법이 셋타1=0.25,셋타2=-1로 초기화된 모델 파라미터를 최적화하는 과정을 보여줌

    여기서도 셋타2=0으로 빠르게 줄어들고 그 다음 축을 따라 진동하면서 전역 최적점(빨간 사각형)에 도착하게 됨

    알파가 증가하면 전역 최적점이 노란점선을 따라 왼쪽으로 이동

    알파가 감소하면 전역 최적점이 오른쪽으로 이동

     

    아래 두 개의 그래프도 동일하지만 l2패널티를 사용

    왼쪽 아래 그래프에서 l2 손실은 원점에 가까울수록 줄어드는 것을 볼 수 있음

    따라서 경사 하강법이 원점까지 직선 경로를 따라 감

    오른쪽 아래 그래프의 등고선은 릿지 회귀의 비용함수를 나타냄(즉 l2손실을 더한 MSE손실함수)

    라쏘와 다른점은 크게 두 가지

    첫째 파라미터가 전역 최적점에 가까워질수록 그레디언트가 작아짐

    따라서 경사하강법이 자동으로 느려지고 수렴에 도움이 됨(진동이 없음)
    둘째 알파를 증가시킬수록 최적의 파라미터(빨간 사각형)가 원점에 더 가까워짐, 하지만 완전히 0이 되지는 않음

     

    라쏘의 비용 함수는 셋타i=0(i=1,2,3,...,n)일 때  미분 가능하지 않음

    하지만 셋타i=0일때 서브그레디언트 벡터 g를 사용하면 경사 하강버을 적용하는데 문제가 없음

    라쏘 회귀의 서브 그레디언트 벡터

    from sklearn.linear_model import Lasso
    lasso_reg=Lasso(alpha=0.1)  #Lasso대신 SGDRegressor(penalty=l1)을 사용할 수도 있음
    lasso_reg.fit(X,y)
    lasso_reg.predict([[1.5]])

    4.5.3 엘라스틱넷

    릿지 회귀와 라쏘 회귀를 절충한 모델

    규제항은 릿지와 회귀의 규제항을 단순히 더해서 사용하며, 혼합 정도는 혼합 비율 r을 사용해 조절

    r=0이면 엘라스틱넷은 릿지 회귀와 같고 r=1이면 라쏘 회귀와 같음

    엘라스틱넷 비용함수

    그럼 보통의 선형회귀(즉, 규제가 없는 모델), 릿지, 라쏘 ,엘라스틱넷을 언제 사용해야 할까?

    적어도 규제가 약간 있는 것이 대부분의 경우에 좋으므로 일반적으로 평범한 선형 회귀는 피해야함

    릿지가 기본이 되지만 쓰이는 특성이 몇 개뿐이라고 의심되면 라쏘나 엘라스틱넷이 나음

    이 모델들은 이전에 이야기한 것처럼 불필요한 특성의 가중치를 0으로 만들어 줌

    특성 수가 훈련 샘플 수보다 많거나 특성 몇개가 강하게 연관되어 있을 떄는 보통 라쏘가 문제를 일으키므로 라쏘보다는 엘라스틱넷을 선호

    from sklearn.linear_model import ElasticNet
    elastic_net=ElasticNet(alpha=0.1,l1_ratio=0.5)
    elastic_net.fit(X,y)
    elastic_net.predict([[1.5]])

    4.5.4 조기 종료

    경사하강법과 같은 반복적인 학습 알고리즘을 규제하는 아주 색다른 방식은 검증 에러가 최솟값에 도달하면 바로 훈련을 중지시키는 것

    이를 조기 종료라고 함

    np.random.seed(42)
    m = 100
    X = 6 * np.random.rand(m, 1) - 3
    y = 2 + X + 0.5 * X**2 + np.random.randn(m, 1)
    
    X_train, X_val, y_train, y_val = train_test_split(X[:50], y[:50].ravel(), test_size=0.5, random_state=10)
    from copy import deepcopy
    
    poly_scaler = Pipeline([
            ("poly_features", PolynomialFeatures(degree=90, include_bias=False)),
            ("std_scaler", StandardScaler())
        ])
    
    X_train_poly_scaled = poly_scaler.fit_transform(X_train)
    X_val_poly_scaled = poly_scaler.transform(X_val)
    
    sgd_reg = SGDRegressor(max_iter=1, tol=-np.infty, warm_start=True,
                           penalty=None, learning_rate="constant", eta0=0.0005, random_state=42)
    #warm_start=True로 지정하면 fit()메서드가 호출될 떄 처음부터 다시 시작하지 않고 이전 모델 파라미터에서 훈련을 이어감
    minimum_val_error = float("inf")
    best_epoch = None
    best_model = None
    for epoch in range(1000):
        sgd_reg.fit(X_train_poly_scaled, y_train)  # 중지된 곳에서 다시 시작합니다
        y_val_predict = sgd_reg.predict(X_val_poly_scaled)
        val_error = mean_squared_error(y_val, y_val_predict)
        if val_error < minimum_val_error:
            minimum_val_error = val_error
            best_epoch = epoch
            best_model = deepcopy(sgd_reg)
    sgd_reg = SGDRegressor(max_iter=1, tol=-np.infty, warm_start=True,
                           penalty=None, learning_rate="constant", eta0=0.0005, random_state=42)
    
    n_epochs = 500
    train_errors, val_errors = [], []
    for epoch in range(n_epochs):
        sgd_reg.fit(X_train_poly_scaled, y_train)
        y_train_predict = sgd_reg.predict(X_train_poly_scaled)
        y_val_predict = sgd_reg.predict(X_val_poly_scaled)
        train_errors.append(mean_squared_error(y_train, y_train_predict))
        val_errors.append(mean_squared_error(y_val, y_val_predict))
    
    best_epoch = np.argmin(val_errors)
    best_val_rmse = np.sqrt(val_errors[best_epoch])
    
    plt.annotate('Best model',
                 xy=(best_epoch, best_val_rmse),
                 xytext=(best_epoch, best_val_rmse + 1),
                 ha="center",
                 arrowprops=dict(facecolor='black', shrink=0.05),
                 fontsize=16,
                )
    
    best_val_rmse -= 0.03  # just to make the graph look better
    plt.plot([0, n_epochs], [best_val_rmse, best_val_rmse], "k:", linewidth=2)
    plt.plot(np.sqrt(val_errors), "b-", linewidth=3, label="Validation set")
    plt.plot(np.sqrt(train_errors), "r--", linewidth=2, label="Training set")
    plt.legend(loc="upper right", fontsize=14)
    plt.xlabel("Epoch", fontsize=14)
    plt.ylabel("RMSE", fontsize=14)
    
    plt.show()

    best_epoch, best_model

    배치경사 하강법으로 훈련시킨 복잡한(고차원 다항 회귀 모델)을 보여줌

    에포크가 진행됨에 따라 알고리즘이 점차 학습되어 훈련 세트에 대한 예측 에러(RMSE)와 검증 세트에 대한 예측 에러가 줄어듦러나 잠시 후 감소하던 검증 에러가 멈춫었다가 다시 상승

    모델이 훈련 데이터에 과대적합되기 시작하는 것을 의미

    조기 종료는 검증 에러가 최소에 도달하는 즉시 훈련을 멈추는 것

    이 규제 테크닉은 매우 효과적이고 간단해서 제프리 힌턴이 '훌륭한 공짜 점심'이라고 불렀음

    4.6 로지스틱 회귀

    4.6.1 확률 추정

    선형 회귀 모델과 같이 로지스틱 회귀 모델은 입력 특성의 가중치 합을 계산하고 편향을 더함

    대신 선형회귀처럼 바로 결과를 출력하지 않고 결괏값의 로지스틱을 출력함

    로지스틱 회귀 모델의 확률 추정

     

     

    댓글

Designed by Tistory.