인공지능

규제적용하기

yul_S2 2023. 1. 10. 15:23
반응형

가중치규제 : 과대적합 해결방법중 하나
가중치의 값이 커지지 않도록 제한하는 기법 
가중치를 규제하면 일반화 성능이 올라간다.

경사가 급한 그래프보다는 경사가 완만한 그래프가 성능이 좋다

알파 : 규제의 양을 조절하는 파라미터
알파값이 크면 전체 손실함수의 값이 커지지 않도록 w의 값의 합이 작아져야한다 : 규제강화
알파값이 작으면 w의 값이 커져도 손실함수 값은 큰폭으로 커지지않는다 : 규제완화 

L1규제의 미분
w_grad += alpha * np.sign(w)
np.sign() : 배열 요소의 부호를 반환한다

절편은 규제하면 모델을 어떤 방향으로 이동시킬 뿐 복잡도에는 영향 없다

회귀모델 손실함수에 l1규제 : 라쏘

 

 

 

로지스틱 회귀에 규제 적용하기

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

#1-1) l1,l2의 기본값은 0

class SingleLayer:
    def __init__(self, learning_rate=0.1, l1=0, l2=0) : #l1,l2 기본값은 0
        self.w = None
        self.b = None
        self.losses = []
        self.val_losses = []
        self.w_history = []
        self.lr = learning_rate

 

 

# 1-2) L1규제와 L2규제의 강도를 조절하는 l1,l2 추가

# L1규제와 L2규제의 강도를 조절하는 l1,l2 추가
        self.l1 = l1
        self.l2 = l2

 

 

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

    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) :
            loss = 0
            #인덱스 섞기
            indexes = np.random.permutation(np.arange(len(x)))   # arrange 아님
            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)  # 역방향 계산

 

 

# 2-1). 그레이디언트에서 패널티 항의 미분값을 더하기

                # 그레이디언트에서 패널티 항의 미분값을 더하기
                w_grad += self.l1 * np.sign(self.w) + self.l2 * self.w
                self.w -= self.lr * w_grad  # 가중치 업데이트(학습률 적용)

 

 

# 2-2) 절편 업데이트 : self.lr * b_grad

                self.b -= b_grad  # 절편 업데이트 : self.lr * 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))

▶ 

 

 

# 2-3) 에포크마다 평균 손실 저장 (+ self.reg_loss()/len(y))

                # 에포크마다 평균 손실 저장
                self.losses.append(loss / len(y) + self.reg_loss()/len(y))
                # 검증 세트에 대한 손실을 계산 ->update_val_loss 메서드 있음
                self.update_val_loss(x_val, y_val)

 

 

# 3. 로지스틱 손실 함수 계산에 패널티 항 추가하기

    def reg_loss(self) :
        return self.l1 * np.sum(np.abs(self.w)) + self.l2 / 2 * np.sum(self.w**2)
    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_losses리스트에 추가
            val_loss += -(y_val[i] * np.log(a) + (1 - y_val[i]) * np.log(1 - a))

 

 

# 4. 검증세트 손실 계산 : update_val_loss() 메서드에서 reg_loss() 호출하기

        self.val_losses.append(val_loss / len(y_val) + self.reg_loss()/len(y_val))
 
layer5 = SingleLayer(l1=0.001)
layer5.fit(x_train_scaled, y_train, epochs=20)
layer5.score(x_val_scaled, y_val)

 

# 5. 최종코드 (단일층 신경망의 SingleLayer 클래스 이용)

class SingleLayer:
    def __init__(self, learning_rate=0.1, l1=0, l2=0) : #l1,l2 기본값은 0
        self.w = None
        self.b = None
        self.losses = []
        self.val_losses = []
        self.w_history = []
        self.lr = learning_rate
        # L1규제와 L2규제의 강도를 조절하는 l1,l2 추가
        self.l1 = l1
        self.l2 = l2
 
    def forpass(self, x):
        z = np.sum(x * self.w) + self.b  # 직선 방정식을 계산
        return z
 
    def backprop(self, x, err):
        w_grad = x * err  # 가중치에 대한 그래디언트를 계산
        b_grad = 1 * err  # 절편에 대한 그래디언트를 계산
        return w_grad, b_grad
 
    def activation(self, z):
        z = np.clip(z, -100, None)  # 안전한 np.exp() 계산을 위해
        a = 1 / (1 + np.exp(-z))  # 시그모이드 계산
        return a
        # fit 메서드에서 역방향 계산 수행할때 그레이디언트 패널티 항의 미분값을 더하기
    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) :
            loss = 0
            #인덱스 섞기
            indexes = np.random.permutation(np.arange(len(x)))   # arrange 아님
            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.lr * 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()/len(y))
                # 검증 세트에 대한 손실을 계산 ->update_val_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)

 

 

 

 

 

# 6. 검증세트 손실 계산 : 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_losses리스트에 추가
            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()/len(y_val))
 
layer5 = SingleLayer(l1=0.001)
layer5.fit(x_train_scaled, y_train, epochs=20)
layer5.score(x_val_scaled, y_val)

 

# 7. cancer데이터 세트에 L1규제 적용하기

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()

 

 

# 8. 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 Curve (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.ylabel('value')
    plt.xlabel('weight')
    plt.ylim(-4, 4)
    plt.show()

 

# 9. 검증세트 손실 계산

layer6 = SingleLayer(l2=0.01)
layer6.fit(x_train_scaled, y_train, epochs=50)
layer6.score(x_val_scaled, y_val)
 
np.sum(layer6.predict(x_val_scaled) == y_val)
 
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)

 

 

반응형