[Do it 딥러닝 입문] 06장

H_jiwoo·2022년 3월 28일
0

딥러닝 공부

목록 보기
6/6

벡터 연산

  • 행렬 연산 = 벡터화된 연산 → 알고리즘의 성능을 높일 수 있음
  • 배치 경사 하강법을 단일 신경망에 적용해 벡터화된 연산을 사용할 수 있음
    • 배치 경사 하강법 : 가중치를 한 번 업데이트 할 떄 전체 샘플 사용 → 손실 함수의 전역 최솟값을 안정적으로 찾을 수 있으나 계산에 비용이 많이 필요

행렬 연산

  • np.dot( ) 함수를 사용하면 점 곱 연산 가능
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)

  • 배치 경사 하강법을 적용한 SingleLayer 클래스
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))

0개의 댓글