728x90
반응형

과대적합(overfitting)

  • 모델이 훈련 세트에서는 좋은 성능을 내지만 검증 세트에서는 낮은 성능을 내는 경우
  • 예) 분류 문제에서 훈련 세트의 정확도가 99%이고 검증 세트의 정확도가 80% 수준이라면 과대적합을 의심할 수 있다.

 

과소적합(underfitthing)

  • 훈련 세트와 검증 세트의 성능에는 차이가 크지 않지만 모두 낮은 성능을 내는 경우

 

훈련 세트의 과대적합, 과소적합 분석

  • 학습 곡선(learning curve)

[그림1] 훈련 세트의 과대적합 과소적합 학습 곡선

첫 번째 학습 곡선 

  • 과대적합의 전형적인 모습
  • 분산이 크다(high variance)라고도 한다.
  • 과대적합 주요 원인 중 하나는 훈련 세트에 충분히 다양한 패턴의 샘플이 포함되지 않은 경우
  • 훈련 세트에 다양한 패턴의 샘플이 없으니 검증 세트에 제대로 적응하지 못한 것이다.
  • 이런 경우 더 많은 훈련 샘플 모아 검증 세트 성능 향상 가능 - 현실적인 한계로 훈련 샘플 더 많이 모을 수 없는 경우도 있다.
  • 이 경우 모델 훈련 세트 집착하지 않도록 가중치 제한 가능 - 모델의 복잡도를 낮춘다.

 

두 번째 학습 곡선

  • 과소적합의 전형적인 모습
  • 훈련 세트와 검증 세트에서 측정한 성능 간격 점점 가까워지지만 성능 자체가 낮다.
  • 편향이 크다(high bias)
  • 훈련 데이터에 있는 패턴 모두 잡아내지 못하는 현상
  • 해결 방법 - 복잡도가 더 높은 모델 사용하거나 가중치 규제 완화

 

세 번째 학습 곡선

  • 과대적합과 과소적합 절충점

 

에포크와 손실 함수의 그래프로 과대적합과 과소적합 분석

[그림2] 에포크와 손실 함수 과대적합과 과소적합 

 

왼쪽 그래프

  • 검증 세트의 손실과 훈련 세트의 손실 나타냄
  • 훈련 세트의 손실은 에포크가 진행될수록 감소, 검증 세트의 손실은 에포크의 횟수가 최적점을 지나면 오히려 상승
  • 최적점 이후에도 훈련 세트로 모델을 학습시키면 모델이 훈련 세트의 샘플에 더 밀착해 학습하기 때문 - 과대적합
  • 최적점 이전 훈련 세트와 검증 세트의 손실이 비슷한 간격 유지하며 함께 줄어든다.  이 영역에서 중지시 - 과소적합

 

오른쪽 그래프

  • 세로 축 손실 대신 정확도 
  • 왼쪽 그래프 뒤집힘

 

에포크 대신 모델 복잡도 사용해 과대적합과 과소적합 분석

[그림3] 모델 복잡도 손실 함수

  • 모델 복잡도란 모델이 가진 학습 가능한 가중치 개수
  • 층의 개수나 유닛의 개수가 많아지면 복잡도가 높은 모델이 만들어짐
  • 너무 많아지면 과대적합

 

적절한 편향-분산 트레이드오프

  • 과소적합 모델 - 편향되었다.
  • 과대적합 모델 - 분산이 크다.

 

편향 - 분산 트레이드오프(bias-variance tradeoff)

  • 과소적합된 모델(편향)과 과대적합된 모델(분산)사이의 관계를 편향-분산 트레이드오프
  • '하나를 얻기 위해서 다른 하나를 희생해야 하기 때문'
  • 편향을 줄이면(훈련 세트의 성능 높임) 분산이 커짐(검증 세트와 성능 차이가 커짐)
  • 반대 분산을 줄이면(검증 세트와 성능 차이 줄임) 편향이 커지는(훈련 세트의 성능이 낮아지는)것을 말한다.

 

1. 검증 손실을 기록하기 위한 변수 추가

# # 1. 검증 손실을 기록하기 위한 변수 추가
# self.val_losses 인스턴스 추가 - 검증 세트에 대한 손실 기록
def __init__(self, learning_rate=0.1):
    self.w = None
    self.b = None
    self.val_losses = []
    self.w_history = []
    self.lr = learning_rate

 

2. fit() 메서드 검증 세트 전달받을 수 있도록 x_val, y_val

# 2. fit() 메서드 검증 세트 전달받을 수 있도록 x_val,y_val 매개변수 추가
def fit(self, x, y, epochs=100, x_val=None, y_val=None):
        self.w = np.ones(x.shape[1])               # 가중치를 초기화합니다.
        self.b = 0                                 # 절편을 초기화합니다.
        self.w_history.append(self.w.copy())       # 가중치를 기록합니다.
        np.random.seed(42)                         # 랜덤 시드를 지정합니다.
        for i in range(epochs):                    # epochs만큼 반복합니다.
            loss = 0
            # 인덱스를 섞습니다
            indexes = np.random.permutation(np.arange(len(x)))
            for i in indexes:                      # 모든 샘플에 대해 반복합니다
                z = self.forpass(x[i])             # 정방향 계산
                a = self.activation(z)             # 활성화 함수 적용
                err = -(y[i] - a)                  # 오차 계산
                w_grad, b_grad = self.backprop(x[i], err) # 역방향 계산
                # 그래디언트에서 페널티 항의 미분 값을 더합니다
                w_grad += self.l1 * np.sign(self.w) + self.l2 * self.w
                self.w -= self.lr * w_grad         # 가중치 업데이트
                self.b -= b_grad                   # 절편 업데이트
                # 가중치를 기록합니다.
                self.w_history.append(self.w.copy())
                # 안전한 로그 계산을 위해 클리핑한 후 손실을 누적합니다
                a = np.clip(a, 1e-10, 1-1e-10)
                loss += -(y[i]*np.log(a)+(1-y[i])*np.log(1-a))
            # 에포크마다 평균 손실을 저장합니다
            self.losses.append(loss/len(y) + self.reg_loss())
            # 검증 세트에 대한 손실을 계산합니다
            self.update_val_loss(x_val, y_val) 

 

3. 검증 손실 계산하기

# 3. 검증 손실 계산하기
# fit() 멧더ㅡ에서 훈련 세트의 손실을 계산하는 방식과 동일
# 검증 세트 샘플 정방향 계산 후 활성화 함수 통과시켜 출력값 계산
# 이 값 로지스틱 손실 함수 계산해 val_losses 리스트 추가
# 에포크마다 update_val_loss 호출
def update_val_loss(self, x_val, y_val):
        if x_val is None:
            return
        val_loss = 0
        for i in range(len(x_val)):
            z = self.forpass(x_val[i])     # 정방향 계산
            a = self.activation(z)         # 활성화 함수 적용
            a = np.clip(a, 1e-10, 1-1e-10)
            val_loss += -(y_val[i]*np.log(a)+(1-y_val[i])*np.log(1-a))
        self.val_losses.append(val_loss/len(y_val) + self.reg_loss())

 

4. 모델 훈련

# 4. 모델 훈련하기
layer3 = SingleLayer()
layer3.fit(x_train_scaled, y_train, x_val=x_val_scaled, y_val=y_val)

 

5. 손실값으로 그래프 그려 에포크 횟수 지정

# 5. 손실값으로 그래프 그려 에포크 횟수 지정
# fit() 메서드 수정해 에포크마다 훈련 세트와 검증 세트의 손실값을 인스턴스 self.val_losses에 저장

plt.ylim(0, 0.3)
plt.plot(layer3.losses)
plt.plot(layer3.val_losses)
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train_loss','val_loss'])
plt.show()
# 검증 손실이 20번째 에포크 이후 훈련 세트보다 높아지는 것

[그림4] 손실값으로 그래프 그려 에포크 횟수 지정

 

6. 훈련 조기 종료

# 6. 훈련 조기 종료 (early stopping)
# 20번째 까지 훈련 다음 검증 성능 확인

layer4 = SingleLayer()
layer4.fit(x_train_scaled, y_train, epochs= 20)
layer4.score(x_val_scaled, y_val) # 97.8% 
# 원래 성능 0.976

[그림5] 정혹도 

728x90
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기