728x90
반응형
가중치 규제(regularization)
- 과대적합을 해결하는 대표적인 방법 중 하나
- 가중치의 값이 커지지 않도록 제한하는 기법
- 가중치 규제시 모델의 일반화 성능이 올라간다.
왼쪽은 간단한 샘플 데이터와 모델 2개 그래프 - 경사가 급한 그래프보다는 경사가 완만한 그래프가 성능이 좋다고 평가
오른쪽은 샘플 데이터에 집착한 그래프
- 샘플 데이터 제대로 표현을 못함
- 새로운 데이터에 적응하지 못하므로 좋은 성능을 가졌다고 할 수 없다.
- '모델이 일반화되지 않았다'
- 이런 상황에 규제를 사용해 가중치를 제한하면 모델이 몇 개의 데이터에 집착하지 않게 되므로 일반화 성능을 높일 수 있다.
- L1, L2규제
L1 규제
- 손실 함수에 가중치의 절댓값인 L1 노름(norm)을 추가한다.
- 소문자 알파벳(w)는 벡터를 의미한다.
- 볼드로 된 대문자 알파벳은 행렬을 의미
- '가중치의 절댓값을 손실 함수에 더한것'
로지스틱 손실 함수에 L1규제 적용
손실 함수에 L1노름 더하면 L1규제 - 규제의 양을 조절하는 파라미터 알파를 곱한 후 더한다.
알파
- L1 규제의 양을 조절하는 하이퍼파라미터
- 알파의 값이 크면 전체 손실 함수의 값이 커지지 않도록 w값의 합이 작아져야 한다.
- 규제가 강해졌다고 표현
- 알파 값이 w의 합이 커져도 손실 함수의 값이 큰 폭으로 커지지 않는다.
- 규제가 약해졌다.
L1 규제의 미분
- 경사 하강법으로 가중치 업데이트 하기 위함
- |w|를 w에 대해 미분 - w가 양수이면 +1 음수이면 -1
- w값을 미분한 결과 w의 부호라는 의미 sign(w)표현
파이썬 코드
- alpha - 규제 하이퍼파라미터
- np.sign() 함수는 배열 요소의 부후ㅗㅗ를 반환한다.
- 절편에 대해 규제하지 않는다. (주의)
- 절편은 모델에 영향을 미치는 방식이 가중치와 다르기 때문이다. 절편 규제시 모델을 어떤 방향으로 이동시킬 뿐 복잡도에는 영향을 주지 않는다.
w_grad += alpha * np.sign(w)
라쏘 모델
- 회귀 모델에 L1 규제를 추가한 것
- 가중치를 줄이다 못해 일부 가중치를 0으로 만들 수도 있다.
- 가중치가 0인 특성은 모델에서 사용할 수 없다는 것과 같은 의미로 특성을 선택하는 효과를 얻을 수 있다.
- L1규제는 알파에 많이 의존한다.
- 가중치에 크기에 따라 규제의 양이 변하지 않으므로 규제 효과가 좋다고 할 수 없다.
L2 규제
- 손실 함수에 가중치에 대한 L2 노름(norm)의 제곱을 더한다.
- 손실 함수에 L2 노름의 제곱을 더하면 L2 규제
- 알파는 L1 규제와 마찬가지로 규제의 양을 조절하기 위한 하이퍼파라미터 1/2은 미분 결과 보기 좋게 하기 위함
L2 규제의 미분
로지스틱 손실 함수의 미분
가중치 업데이트 식에 대입
파이썬 코드
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()
규제 적용하지 않은 검증 세트 성능 평가와 같다.
layer5 = SingleLayer(l1=0.001)
layer5.fit(x_train_scaled, y_train, epochs=20)
layer5.score(x_val_scaled,y_val) #0.978~
# 규제 적용하지 않고 검증 세트 성능 평가와 같다
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()
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 데이터 샘플이 적어서 큰 차이가 없다.
np.sum(layer6.predict(x_val_scaled) == y_val) # 91개 검증 샘플 중 89개 옳게 예측
# L1 규제보다 에포크가 크게 늘어남 L1 ㄱ제시 20번 에포크 - L2 규제 50번의 에포크
# 가중치 강하게 제한해서 검증 세트의 손실값을 일절 수준으로 유지하면서 알고리즘이 전역 최솟갑승ㄹ 찾는 과정 오래 반복가능
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)
728x90
반응형
최근댓글