728x90
반응형

가중치 규제(regularization)

  • 과대적합을 해결하는 대표적인 방법 중 하나
  • 가중치의 값이 커지지 않도록 제한하는 기법
  • 가중치 규제시 모델의 일반화 성능이 올라간다.

[그림1] 가중치 규제 그래프

왼쪽은 간단한 샘플 데이터와 모델 2개 그래프 - 경사가 급한 그래프보다는 경사가 완만한 그래프가 성능이 좋다고 평가

 

오른쪽은 샘플 데이터에 집착한 그래프

  • 샘플 데이터 제대로 표현을 못함
  • 새로운 데이터에 적응하지 못하므로 좋은 성능을 가졌다고 할 수 없다.
  • '모델이 일반화되지 않았다'
  • 이런 상황에 규제를 사용해 가중치를 제한하면 모델이 몇 개의 데이터에 집착하지 않게 되므로 일반화 성능을 높일 수 있다.
  • L1, L2규제

 

L1 규제

  • 손실 함수에 가중치의 절댓값인 L1 노름(norm)을 추가한다.
  • 소문자 알파벳(w)는 벡터를 의미한다.
  • 볼드로 된 대문자 알파벳은 행렬을 의미
  • '가중치의 절댓값을 손실 함수에 더한것'

[그림2] L1규제 정의

 

로지스틱 손실 함수에 L1규제 적용

[그림3] 로지스틱 손실 함수에 L1 규제 적용

손실 함수에 L1노름 더하면 L1규제 - 규제의 양을 조절하는 파라미터 알파를 곱한 후 더한다.

[그림4] 규제의 양 조절

알파

  • L1 규제의 양을 조절하는 하이퍼파라미터
  • 알파의 값이 크면 전체 손실 함수의 값이 커지지 않도록 w값의 합이 작아져야 한다.
    • 규제가 강해졌다고 표현
  • 알파 값이 w의 합이 커져도 손실 함수의 값이 큰 폭으로 커지지 않는다.
    • 규제가 약해졌다.

L1 규제의 미분

  • 경사 하강법으로 가중치 업데이트 하기 위함
  • |w|를 w에 대해 미분  - w가 양수이면 +1 음수이면 -1
  • w값을 미분한 결과 w의 부호라는 의미 sign(w)표현

[그림5] L1규제를 적용한 손실함수의 도함수

 

[그림6] L1규제에 학습률도 적용

 

파이썬 코드

  • alpha - 규제 하이퍼파라미터 
  • np.sign() 함수는 배열 요소의 부후ㅗㅗ를 반환한다.
  • 절편에 대해 규제하지 않는다. (주의)
  • 절편은 모델에 영향을 미치는 방식이 가중치와 다르기 때문이다. 절편 규제시 모델을 어떤 방향으로 이동시킬 뿐 복잡도에는 영향을 주지 않는다.
w_grad += alpha * np.sign(w)

 

 

라쏘 모델

  • 회귀 모델에 L1 규제를 추가한 것
  • 가중치를 줄이다 못해 일부 가중치를 0으로 만들 수도 있다.
  • 가중치가 0인 특성은 모델에서 사용할 수 없다는 것과 같은 의미로 특성을 선택하는 효과를 얻을 수 있다.
  • L1규제는 알파에 많이 의존한다.
    • 가중치에 크기에 따라 규제의 양이 변하지 않으므로 규제 효과가 좋다고 할 수 없다.

 

L2 규제

  • 손실 함수에 가중치에 대한 L2 노름(norm)의 제곱을 더한다.

[그림7] L2 규제 정의

  • 손실 함수에 L2 노름의 제곱을 더하면 L2 규제 
  • 알파는 L1 규제와 마찬가지로 규제의 양을 조절하기 위한 하이퍼파라미터 1/2은 미분 결과 보기 좋게 하기 위함

[그림8] 손실 함수 L2노름 식

 

L2 규제의 미분

 

로지스틱 손실 함수의 미분

[그림9] 로지스틱 손실 함수의 미분

 

가중치 업데이트 식에 대입

[그림10] 가중치 업데이트 식 대입 - 학습률 파라미터

 

파이썬 코드

w_grad += alpha * w 

 

L2 규제는 그레이디언트 계산에 가중치의 값 자체가 포함되므로 가중치 부호만 사용하는 L1 규제보다 조금 더 효과적

L2는 가중치를 완전히 0으로 만들지 않는다. - 0으로 만들 시 특성 제외 효과 대신 모델 복잡도가 떨어진다.

 

 

릿지(Ridge) 모델

  • 회귀 모델에 L2 규제를 적용한 것
  • 사이킷런에서는 릿지 모델 sklearn.linear_model.Ridge클래스 제공
  • penalty 매개변수를 l2지정해 L2 규제 추가 가능
  • 두 클래스 모두 규제 강도는 alpha 매개변수로 제어

 

L1 규제

  • 그레이디언트에서 alpha 가중치의 부호를 곱해 그레이디언트에 더한다.
w_grad += alpha * np.sign(w)

L2 규제

  • 그레이디언트에서 alpha 가중치를 곱해 그레이디언트에 더한다.
w_grad += aplha * w

 

 

로지스틱 회귀에 규제 적용

 

1. 그레이디언트 업데이트 수식에 페널티 항 반영

# 1. 그레이디언트 업데이트 수식에 페널티 항 반영하기

def __init__(self, learning_rate=0.1, l1=0, l2=0):
    self.w = None
    self.b = None
    self.losses = []
    self.val_loses = []
    self.w_history = []
    self.lr = learning_rate
    self.l1 = l1
    self.l2 = l2

 

 

2. fit 메서드 역방향 계산 수행시 그레이디언트에 페널티 항의 미분값 더하기

# 2. fit 메서드 역방향 계싼 수행시 그레이디언트에 페넡리 항의 미분값을 더한다. 
# L1, L2 규제 동시에 가능 

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. 로지스틱 손실 함수 계산에 페넡티 항 추가하기
# 페널티 항에 대한 값 더해야 한다. reg_loss()메서드 추가
def reg_loss(self):
    return self.l1 * np.sum(np.abs(self.w)) + self.l2 / 2 * np.sum(self.w**2)
    

 

4. 검증 세트 손실 계산하는 update_val_loss() 메서드에 reg_loss() 호출 설정

# 4. 검증 세트 손실 계산하는 update_val_loss() 메서드에 reg_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())

 

 

5. cancer 데이터 세트에 L1 규제 적용

  • 규제 강도 0.0001, 0.001, 0.01 규제 강도에 따라 모델의 학습 곡선과 가중치 어떻게 바뀌는지 확인
# 5. cancer 데이터 세트에 L1 규제 적용
# 규제 강도 0.0001, 0.001, 0.01 규제 강도에 따라 모델의 학습 곡선과 가중치 어떻게 바뀌는지 확인
l1_list = [0.0001, 0.001, 0.01]

for l1 in l1_list:
    lyr = SingleLayer(l1=l1)
    lyr.fit(x_train_scaled, y_train, x_val=x_val_scaled, y_val =y_val)
    
    plt.plot(lyr.losses)
    plt.plot(lyr.val_losses)
    plt.title('Learning Curve (l1={})'.format(l1))
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.legend(['train_loss', 'val_loss'])
    plt.ylim(0, 0.3)
    plt.show()
    
    
    plt.plot(lyr.w, 'bo')
    plt.title('Weight (l1={})'.format(l1))
    plt.ylabel('value')
    plt.xlabel('weight')
    plt.ylim(-4, 4)
    plt.show()

[그림11] 규제강도 0.0001
[그림12] 규제강도 0.001
[그림13] 규제강도 0.01

 

규제 적용하지 않은 검증 세트 성능 평가와 같다.

layer5 = SingleLayer(l1=0.001)
layer5.fit(x_train_scaled, y_train, epochs=20)
layer5.score(x_val_scaled,y_val) #0.978~
# 규제 적용하지 않고 검증 세트 성능 평가와 같다

[그림14] 평가, 정확도

 

 

6. cancer 데이터 세트에 L2 규제 적용

# 6. cancer 데이터 세트에 L2 규제 적용

l2_list = [0.0001, 0.001, 0.01]

for l2 in l2_list:
    lyr = SingleLayer(l2=l2)
    lyr.fit(x_train_scaled, y_train, x_val=x_val_scaled, y_val=y_val)
    
    plt.plot(lyr.losses)
    plt.plot(lyr.val_losses)
    plt.title('Learning Cureve (l2={})'.format(l2))
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.legend(['train_loss','val_loss'])
    plt.ylim(0, 0.3)
    plt.show()
    
    plt.plot(lyr.w, 'bo')
    plt.title('Weight (l2={})'.format(l2))
    plt.xlabel('weight')
    plt.ylabel('value')
    plt.ylim(-4, 4)
    plt.show()
    

[그림15] 규제강도 0.0001 
[그림16] 규제강도 0.001
[그림17] 규제강도 0.01

 

 

cancer 데이터 샘플이 적어서 큰 차이가 없다.

layer6 = SingleLayer(l2=0.01)
layer6.fit(x_train_scaled, y_train, epochs=50)
layer6.score(x_val_scaled, y_val) # 0.978 
# L1 규제와 동일
# cancer 데이터 샘플이 적어서 큰 차이가 없다.

[그림18] L2 정확도 평가

 

np.sum(layer6.predict(x_val_scaled) == y_val) # 91개 검증 샘플 중 89개 옳게 예측

# L1 규제보다 에포크가 크게 늘어남 L1 ㄱ제시 20번 에포크 - L2 규제 50번의 에포크
# 가중치 강하게 제한해서 검증 세트의 손실값을 일절 수준으로 유지하면서 알고리즘이 전역 최솟갑승ㄹ 찾는 과정 오래 반복가능

[그림19] 예측한 숫자

7. SGDClassifier에서 규제 적용

# 7. SGDClassifier 에서 규제 사용

sgd = SGDClassifier(loss='log', penalty='l2', alpha=0.001, random_state=42)
sgd.fit(x_train_scaled, y_train)
sgd.score(x_val_scaled, y_val)

[그림20] sgdclassifier에서 규제 사용

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