z = np.sum(x*self.w)+self.b
=>
z = np.dot(x,self.w)+self.b
import numpy as np
import matplotlib.pyplot as plt
#위스콘신 유방암 데이터 불러오기
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
#위스콘신 유방암 데이터 세트를 훈련, 검증, 테스트 세트로 나누기
cancer = load_breast_cancer()
x = cancer.data
y = cancer.target
x_train_all,x_test,y_train_all,y_test = train_test_split(x,y,stratify=y,test_size=0.2,random_state=42)
x_train,x_val,y_train,y_val = train_test_split(x_train_all,y_train_all,stratify=y_train_all,test_size=0.2,random_state=42)
#훈련 세트와 검증 세트의 크기확인
print(x_train.shape,x_val.shape)
(364, 30) (91, 30)
class SingleLayer:
def __init__(self,learning_rate=0.1,l1=0,l2=0):
self.w = None
self.b = None
self.losses=[] #훈련 손실
self.val_losses =[] #검증 손실
self.w_history=[] #기중치 기록
self.lr=learning_rate #학습률
self.l1=l1 #L1 손실 하이퍼파라미터
self.l2=l2 #L2 손실 하이퍼파라미터
#forpass(), backprop()메서드에 배치 경사 하강법 적용
def forpass(self,x):
z=np.dot(x,self.w)+self.b #행렬 연산이기 때문에 np.dot()사용
return z
def backprop(self,x,err):
m=len(x) #넘파이 행렬의 행 크기 반환
w_grad = np.dot(x.T,err)/m
b_grad = np.sum(err)/m #오차 행렬의 평균값
return w_grad, b_grad
def activation(self,z):
a=1/(1+np.exp(-z))
return a
def fit(self, x, y, epochs=100, x_val=None, y_val=None):
y=y.reshape(-1,1)
y_val=y_val.reshape(-1,1)
m=len(x)
self.w = np.ones(x.shape[1])
self.b = 0
self.w_history.append(self.w.copy()) #w_history에 가중치 기록 / 넘파이 배열을 리스트에 추가하면 실제 값 복사가 아닌 배열 참조 -> 가중치 변수 self.w의 값이 바뀔 떄마다 그 값을 복사해 w_history에 추가해야 함
for i in range(epochs):
z = self.forpass(x) #정방향 계산
a = self.activation(z) #활성화 함수 적용
err = -(y-a) #오차 계산
w_grad, b_grad = self.backprop(x,err) #오차를 역전파 해 그레디언트 계산
w_grad += (self.l1*np.sign(self.w)+self.l2*self.w)/m #그레디언트에서 페널티 항의 미분값을 더함
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) #np.clip() : 주어진 범위 밖의 값을 범위 양 끝의 값으로 잘라냄.
#로그 손실과 규제 손실을 더하여 리스트에 추가
loss += np.sum(-(y*np.log(a)+(1-y[i])*np.log(1-a)))
self.losses.append((loss+self.reg_loss())/m)
#검증 세트에 대한 손실 계산
slef.upate_val_loss(x_val,y_vaㅣ)
def predict(self,x):
z = self.forpass(x) #정방향 게산 수행
return z > 0 #스텝 함수 적용
def score(self,x,y):
return np.mean(self.predict(x)==y,reshape(-1,1)) #예측과 타깃 열 벡터를 비교하여 True의 비율을 반환
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):
z=self.forpass(x_val) #정방향 계산 수행
a=self.activation(z) #활성화 함수 적용
a=np.clip(a,1e-10,1-1e-10) #출력값 클리핑
#로그 손실과 규제 손실을 더하여 리스트에 추가
val_loss = np.sum(-(y_val*np.log(a)+(1-y_val)*np.log(1-a)))
self.val_losses.append((val_loss+self.reg_loss())/len(y_val))