전처리(2)
6. 가중치를 기록할 변수와 학습률 파라미터 추가
# 1. SingleLayer클래스에 변수를 추가
class SingleLayer: def __init__(self, learning_rate=0.1): self.w = None self.b = None self.losses = [] self.w_history = [] ### 인스턴스 변수 w_history 만들기 self.lr = learning_rate ### 학습률 파라미터 적용 |
▶
w_history는 가중치를 기록하는 변수이다.
가중치 바뀔 때마다 w_history 리스트에 가중치 기록
넘파이 배열을 리스트에 추가하면 실제 값이 복사되는 것이 아니라 배열을 참조하기 때문에 가중치 변수 self.w의 값이 바뀔 때마다 그 값을 복사하여 w_history 리스트에 추가해야한다.
w_grad에 학습률 self.lr을 곱하는 연산을 추가하여 가중치 업데이트 양을 조절한다.
learning_rate는 하이퍼 파라미터이다.
가중치의 업데이트 양을 조절할 목적으로 사용된다.
학습률에 따라 최적의 해를 구하는 과정이 달라질 수 있다.
학습률이 너무 커도 작아도 안정적인 학습이 불가능하다.
보통 0.001, 0.01등의 로그 스케일로 학습률을 지정한다.
# 3. 가중치 기록하고 업데이트 양 조절하기
def fit(self,x,y,epochs=100): 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) #역방향 계산 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)) |
▶
w_history는 가중치를 기록하는 변수이다.
가중치 바뀔 때마다 w_history 리스트에 가중치 기록
넘파이 배열을 리스트에 추가하면 실제 값이 복사되는 것이 아니라 배열을 참조하기 때문에 가중치 변수 self.w의 값이 바뀔 때마다 그 값을 복사하여 w_history 리스트에 추가해야한다.
w_grad에 학습률 self.lr을 곱하는 연산을 추가하여 가중치 업데이트 양을 조절한다.
# 4. 최종코드 (단일층 신경망의 SingleLayer 클래스 이용)
class SingleLayer: def __init__(self, learning_rate=0.1): self.w = None self.b = None self.losses = [] self.w_history = [] ### 인스턴스 변수 w_history 만들기 self.lr = learning_rate ### 학습률 파라미터 적용 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 def fit(self,x,y,epochs=100): 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) #역방향 계산 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)) def predict(self, x): z = [self.forpass(x_i) for x_i in x] # 정방향 계산 return np.array(z) > 0 # 스텝 함수 적용 def score(self, x, y): return np.mean(self.predict(x) == y) |
# 5. 모델 훈련하고 평가
layer1 = SingleLayer() layer1.fit(x_train, y_train) print(layer1.score(x_val, y_val)) |
# 0.9120879120879121 |
# 6. 그래프
w2 = [] w3 = [] for w in layer1.w_history: w2.append(w[2]) w3.append(w[3]) plt.plot(w2, w3) plt.plot(w2[-1], w3[-1], 'ro') plt.xlabel('w[2]') plt.ylabel('w[3]') plt.show() |
▶layer1 객체의 인스턴스 변수 w_history에는 100번의 에포크 동안 변경된 가중치가 기록되어있다.
세번째요소(w[2]), 네번째요소(w[3])는 각각 mean perimeter와 mean area 특성에 대한 가중치이다.
최종으로 결정된 가중치는 점으로 표시

w[2]의 스케일은 0~2500이고, w[3]의 스케일은 0~700이기 때문에 상대적으로 작은 w[3]의 값이 크게 변동된다.
이런 현상을 스케일 조정으로 줄일 수 있다.