규제 적용
로지스틱 회귀에 규제를 적용해보자.
앞에서 만들어 놓은 SingleLayer 클래스에 L1 규제와 L2 규제를 적용하자.
def __init__(self):
self.w = None
self.b = None
self.losses = []
self.val_losses = []
self.w_history =[]
self.lr = learning_rate
self.l1 = l1
self.l2 = l2
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(2)
for i in range(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) #검증 세트 손실 계산
def reg_loss(self):
return self.l1 * np.sum(np.abs(self.w)) + self.l2 / 2 * np.sum(self.w**2)
검증 세트의 손실을 계산하는 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))
slef.val_losses.append(val_loss/len(y_val) + self.reg_loss())
L1 규제 적용
L1 규제의 강도를 0.0001, 0.001, 0.01 세가지로 훈련을 해보자



학습 곡선 그래프를 보면 규제가 더 커질수록 훈련 세트의 손실과 검증 세트의 손실이 모두 높아진다.
즉, 과소적합 현상이 나타난다.
L1 값이 커질수록 가중치의 값이 0에 가까워진다.
가장 적절한 l1은 0.001 정도인 것 같다.
L2 규제 적용
L2 규제도 0.0001, 0.001, 0.01을 적용해 보자.



L2 규제도 L1 규제와 비슷한 양상을 보인다.
L2 규제는 규제 강도가 강해져도 L1 규제만큼 과소 적합이 심해지지는 않는다.
가중치도 0에 너무 가까게 줄어들지 않는다.
규제 적용
로지스틱 회귀에 규제를 적용해보자.
앞에서 만들어 놓은 SingleLayer 클래스에 L1 규제와 L2 규제를 적용하자.
def __init__(self):
self.w = None
self.b = None
self.losses = []
self.val_losses = []
self.w_history =[]
self.lr = learning_rate
self.l1 = l1
self.l2 = l2
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(2)
for i in range(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) #검증 세트 손실 계산
def reg_loss(self):
return self.l1 * np.sum(np.abs(self.w)) + self.l2 / 2 * np.sum(self.w**2)
검증 세트의 손실을 계산하는 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))
slef.val_losses.append(val_loss/len(y_val) + self.reg_loss())
L1 규제 적용
L1 규제의 강도를 0.0001, 0.001, 0.01 세가지로 훈련을 해보자



학습 곡선 그래프를 보면 규제가 더 커질수록 훈련 세트의 손실과 검증 세트의 손실이 모두 높아진다.
즉, 과소적합 현상이 나타난다.
L1 값이 커질수록 가중치의 값이 0에 가까워진다.
가장 적절한 l1은 0.001 정도인 것 같다.
L2 규제 적용
L2 규제도 0.0001, 0.001, 0.01을 적용해 보자.



L2 규제도 L1 규제와 비슷한 양상을 보인다.
L2 규제는 규제 강도가 강해져도 L1 규제만큼 과소 적합이 심해지지는 않는다.
가중치도 0에 너무 가까게 줄어들지 않는다.