ABOUT ME

Contact.
Email:yj.anthonyjo@gmail.com
Introduce : CS Student.

Today
-
Yesterday
-
Total
-
  • 딥러닝 공부 - 신경망 첫걸음 정리_6_실습_신경망 코드 작성
    인공지능, 머신러닝 2020. 1. 30. 10:50
    반응형

    Jupyter Notebook 파일

    신경망 코드와 논리 순서.ipynb
    0.03MB

    신경망 뼈대코드

    신경망에 들어가야할 기본적인 코드는 다음과 같다.

    • 값 초기화 코드 : 각 계층의 노드 수, 가중치, 학습률의 값을 설정해준다. (__init__()메서드)
    • 학습 코드 : 가중치를 수정하는 부분의 코드이다. (train()메서드)
    • 질의 코드 : 현재의 가중치를 가지고 질의하여 결과를 얻는 부분의 코드이다 (query()메서드)

    신경망 뼈대 코드


    값 초기화

    값 초기화 부터 해보자. 

    • 각 계층의 노드 개수와, 학습률 설정
      (이전에 이야기 했듯이, 하나의 클래스를 신경망으로 사용한다. 그리고 아래 그림에 적어놨듯이, 객체를 선언할때, 매개변수로만 __init__()함수에 전달 해주면 된다. 그리하여, 신경망이 필요할 때 마다 새롭게 코드를 작성하지 않고도, 신경망을 사용할 수 있다. 편리하다.) 여기서는 각 계층 3개의 노드, 학습률은 0.3으로 설정하였다.

    • 가중치 초기화
      두가지 방법이 있었다. 그러나 여기서는 최종적인 방법만 사용하자.(하나는 그냥 Random하게 하는 것이다.)
      이전 포스팅에서 가중치는 1/ Root(현재노드에 들어오는 이전 노드 입력 값의 개수) 를 표준편차로 하는, 정규분포였다. 정규분포는 0을 평균으로, 표준편차는 이것을 사용한 Random값이다. 정규분포는 Numpy에서 다음과 같이 구현한다.
      np.random.normal(평균, 표준편차, 값들 저장할 배열(행렬)) (아마 normal이 정규분포를 의미하지 않을까.)

     


    질의하기

    지금은 3계층이다. 이를 적용하자. (행렬곱 이용)

    1. Input Layer로 들어오는 값 : I (Input Layer는 활성화 함수를 적용하지 않는다. (관용, 역사라고 한다.))
    2. Hidden Layer로 들어오는 값 : X_h = W_ih * I
    3. Hidden Layer에서 나가는 값 : O_h = Sigmoid(X_h)
    4. Output Layer로 들어오는 값 : X_o = W_hi * O_h
    5. Output Layer에서 나가는 최종 값 : O_o = Sigmoid(X_o)

    Numpy에서, 행렬곱은, np.dot(좌측 행렬, 우측행렬) 로 이용한다.

    1~5과정을 적용한 신경망 Class
    결과
    행렬 곱에 대한 실험


    학습하기

    절차

    1. query(질의)와 마찬가지로 질의 결과를 받아오기(O_o)
    2. E_o = (T - O_o)
    3. E_h = W_ho^T(W_ho의 전치) * E_o
    4. 가중치 수정 
      1. W_ho(new) = W_ho + [(alpha(가중치)) * E_o *O_o*(1-O_o)] x O_h^T (O_h의 전치)
      2. W_ih(new) = W_ih + [(alpha(가중치)) * E_h *O_h*(1-O_h)] x O_i^T (O_i의 전치 = input list의 전치, sigmoid함수 적용 안함)


    최종코드 (실제로는 아님)

    실제로는 아니라고 했는데, 이유는, 내가 학습률을 1주기에서 올리기 위해, 가중치를 E_o 가 0이 될때까지 학습을 시켰기 때문이다.(내가 추가한 해당부분은 그림으로 표시하겠다.)

    실제로는 이렇게 하면 안된다. 왜냐하면, 입력값이 해당 값만 있는 것이 아니기에, 이렇게 하면, 다른 입력값에 유연하게 대처하지 못한다. 그래서, 입력값들을 모두 넣어 학습시키고 또 그것을 반복하는 방법을 사용한다. 이때, 한번 학습의 수행을 주기(epoch)라고 한다. 주기 수를 늘리는 방법을 사용한다. ( 반대로 주기 수를 너무 늘리면 오버피팅(과적합,overfitting)이 일어나는데, 이는 나중에 설명하겠다.(다음 MNIST편) )


    최종코드 (완성)

    이게 최종 완성된 코드이다.


    사용된 코드

    • 신경망 기초 뼈대 코드
    #다음이 신경망의 기초 뼈대 코드
    class neuralNet:
        def __init__(self):
            pass
        def train(self):
            pass
        def query(self):
            pass
    pass

    • 노드수, 학습률 설정
    class neuralNet_v1: #node수 받아오기
        def __init__(self,in_node_num,hidden_node_num,out_node_num,learning_rate):
            self.i_nodes = in_node_num  #매개변수를 사용할 때는 self를 붙이지 않는다.(아마 메서드 자체에 들어온 것이어서 필요 없는 것이 아닐까 싶다.)
            self.h_nodes = hidden_node_num
            self.o_nodes = out_node_num
            
            self.l_r = learning_rate
            print(self.i_nodes, self.h_nodes, self.o_nodes, self.l_r)
            pass
        def train(self):
            pass
        def query(self):
            pass
    pass
    
    #layer별 node 개수 설정
    in_node_num = 3
    hidden_node_num = 3
    out_node_num = 3
    
    learning_rate=0.3
    n1 = neuralNet_v1(in_node_num,hidden_node_num,out_node_num,learning_rate)
    

    • 가중치 초기화
    import numpy as np
    """ #코드 설명
    #-1/root(node_num) ~ 1/root(node_num) 만들기
    W_ih_v3 = np.random.normal(0.0 , 1/ pow(hidden_node_num,1/2) , (hidden_node_num ,in_node_num)) 
    #매게변수 : 정규분포 평균, 표준편차, (행, 열) 
    #(hidden_node_num,in_node_num)인 이유는 (가로에 목적지 동일한 게) 들어가고, (세로에 출발지 동일한 게) 들어가므로
    #(목적시 수가 즉 행의 개수), (출발지 수가 즉 열의 개수)를 의미하기 때문이다. 
    # 이 함수는 다름아닌.. 정규분포를 만들고 그 안에서 값들을 무작위로 만든다.
    print(W_ih_v3)
    
    ###-----------------------------###
    ##모든 가중치 배열 생성하기
    W_ih = np.random.normal(0.0 , 1/ pow(hidden_node_num,1/2) , (hidden_node_num ,in_node_num))
    W_ho = np.random.normal(0.0 , 1/ pow(hidden_node_num,1/2) , (out_node_num ,hidden_node_num))
    #print(W_ih , W_ho)
    
    """
    class neuralNet_v2: #node수 받아오기 + 가중치 초기화
        def __init__(self,in_node_num,hidden_node_num,out_node_num,learning_rate):
            self.i_nodes = in_node_num
            self.h_nodes = hidden_node_num
            self.o_nodes = out_node_num
            
            self.l_r = learning_rate
            self.W_ih = np.random.normal(0.0 , 1/ pow(self.h_nodes,1/2) , (self.h_nodes ,self.i_nodes))
            self.W_ho = np.random.normal(0.0 , 1/ pow(self.o_nodes,1/2) , (self.o_nodes ,self.h_nodes))
            print(self.W_ih, self.W_ho)
            pass
        def train(self):
            pass
        def query(self):
            pass
    pass
    
    #####
    in_node_num = 3
    hidden_node_num = 3
    out_node_num = 3
    
    learning_rate=0.3
    
    n2 = neuralNet_v2(in_node_num, hidden_node_num, out_node_num, learning_rate)

    • 질의하기
    import numpy as np
    import scipy.special as sig
    """
    #lamdba사용하기!!!
    #이미 라이브러리로 정의되있는 함수의 경우, 우리가 alias를 붙이듯이 다시 이름을 정의할 수 있다 바로 lamdba를 사용하는 것이다.
    Sigmoid = lambda x: sig.expit(x)  # Sigmoid의 매개변수를 x에 전달해준다.
    print(Sigmoid(1))
    """
    class neuralNet_v3: #node수 받아오기 + 가중치 초기화
        def __init__(self,in_node_num,hidden_node_num,out_node_num,learning_rate):
            self.i_nodes = in_node_num
            self.h_nodes = hidden_node_num
            self.o_nodes = out_node_num
            
            self.l_r = learning_rate
            
            self.W_ih = np.random.normal(0.0 , 1/ pow(self.h_nodes,1/2) , (self.h_nodes ,self.i_nodes))
            self.W_ho = np.random.normal(0.0 , 1/ pow(self.o_nodes,1/2) , (self.o_nodes ,self.h_nodes))
            print(self.W_ih, self.W_ho)
            pass
        def train(self):
            pass
        def query(self, input_list): 
            #if input_list 의 type이 numpy가 아니라면, 변환하는 과정이 필요하다. 만약 1차원 배열이 input_list라면, np.array(input_list,2).T를 통해 열을 사용하는 numpy array 로 변환해야한다.
            print(input_list.shape)
            self.X_h = np.dot(self.W_ih,input_list)
            self.O_h = sig.expit(self.X_h) 
            #or self.Sigmoid = lamdba x: sig.expit(x) 를 위에 써주고, 이 라인에 self.O_h=self.Sigmoid(self.X_h)해도 된다.
            
            self.X_o = np.dot(self.W_ho, self.O_h)
            self.O_o = sig.expit(self.X_o)
            """print("X_h ---------------------- ")
            print(self.X_h)
            print("O_h ---------------------- ")
            print(self.O_h)
            print("X_o ---------------------- ")
            print(self.X_o)
            print("O_o ---------------------- ")
            print(self.O_o)"""
            return O_o
    pass
    
    #####
    in_node_num = 3
    hidden_node_num = 3
    out_node_num = 3
    
    learning_rate=0.3
    
    n3 = neuralNet_v3(in_node_num,hidden_node_num,out_node_num,learning_rate)
    n3.query(np.array([1,1,1],ndmin=2).T) #이 경우 행만 있는 배열을, numpy 배열로 바꾸어주고 전치 시켜서 열의 배열로 바꾸어 주었다.
    # = n3.query(np.array([[1],[1],[1]]))와 동일

    • 가중치 학습하기
    import numpy as np
    import scipy.special as sig
    class neuralNet_v4: # 여기서는 반항심에, query를 실행했다고 가정하고, Train을 작성하였다. 따라서, train에는 질의하는 부분이 없다.
        def __init__(self,in_node_num,hidden_node_num,out_node_num,learning_rate):
            self.i_nodes = in_node_num
            self.h_nodes = hidden_node_num
            self.o_nodes = out_node_num
            
            self.l_r = learning_rate
            
            self.W_ih = np.random.normal(0.0 , 1/ pow(self.h_nodes,1/2) , (self.h_nodes ,self.i_nodes))
            self.W_ho = np.random.normal(0.0 , 1/ pow(self.o_nodes,1/2) , (self.o_nodes ,self.h_nodes))
            print(self.W_ih, self.W_ho)
            pass
        def train(self,target_list):
            """step 1 : target_list받아오기
            step 2 : E_o 구하기(list형태)
            step 3 : 오차 재분배 하기 (모든 layer)
            step 4 : 모든 layer의 W의 (delta)W를 구해서 적용하기 OR 바로 적용하기 (new)W = (old)W - (self.l_r * (delta)E/(delta)W)
            """
            #step 1 : success
            
            #step 2
            self.E_o = target_list - self.O_o
            #step 2 :success
            
            #step 3
            self.E_h = np.dot(self.W_ho.T, self.E_o)
            #step 3 : success
            #print(self.E_o)
            #print(self.E_h)
            #print(self.W_ho)
            #print("--------------before/after--------------")
            #step 4
            self.W_ho = self.W_ho + ( self.l_r * np.dot(( self.E_o*self.O_o*(1.0-self.O_o) ),self.O_h.T) )
            self.W_ih = self.W_ih + ( self.l_r * np.dot(( self.E_h*self.O_h*(1.0-self.O_h) ),self.il.T) )
            print(self.W_ho)
            print(self.W_ih)
            #step 4 : success
            pass
        def query(self, input_list): 
            #if input_list 의 type이 numpy가 아니라면, 변환하는 과정이 필요하다. 만약 1차원 배열이 input_list라면, np.array(input_list,2).T를 통해 열을 사용하는 numpy array 로 변환해야한다.
            print(input_list.shape)
            self.il = input_list
            self.X_h = np.dot(self.W_ih,input_list)
            self.Sigmoid = lambda x: sig.expit(x)
            self.O_h=self.Sigmoid(self.X_h)
            
            self.X_o = np.dot(self.W_ho, self.O_h)
            self.O_o = sig.expit(self.X_o)
            return self.O_o
    pass
    
    #####
    in_node_num = 3
    hidden_node_num = 3
    out_node_num = 3
    
    learning_rate=0.3
    
    n4 = neuralNet_v4(in_node_num,hidden_node_num,out_node_num,learning_rate)
    print("before train")
    print(n4.query(np.array([1,2,3],ndmin=2).T))
    print("--------")
    n4.train(np.array([0.5,0.3,0.2],ndmin=2).T)

    • 최종 코드
    import numpy as np
    import scipy.special as sig
    class neuralNet_v6:
        def __init__(self,in_node_num,hidden_node_num,out_node_num,learning_rate):
            self.i_nodes = in_node_num
            self.h_nodes = hidden_node_num
            self.o_nodes = out_node_num
            
            self.l_r = learning_rate
            
            self.W_ih = np.random.normal(0.0 , 1/ pow(self.h_nodes,1/2) , (self.h_nodes ,self.i_nodes))
            self.W_ho = np.random.normal(0.0 , 1/ pow(self.o_nodes,1/2) , (self.o_nodes ,self.h_nodes))
            pass
        def train(self,input_list,target_list):
            self.Sigmoid = lambda x: sig.expit(x)
            """step 1 : target_list받아오기
            step 2 : E_o 구하기(list형태)
            step 3 : 오차 재분배 하기 (모든 layer)
            step 4 : 모든 layer의 W의 (delta)W를 구해서 적용하기 OR 바로 적용하기 (new)W = (old)W - (self.l_r * (delta)E/(delta)W)
            """
            #step 2
            self.X_h = np.dot(self.W_ih,input_list)
            self.O_h=self.Sigmoid(self.X_h)
            
            self.X_o = np.dot(self.W_ho, self.O_h)
            self.O_o = sig.expit(self.X_o)
            
            self.E_o = target_list - self.O_o
            #step 2 :success
    
            #step 3
            self.E_h = np.dot(self.W_ho.T, self.E_o)
            #step 3 : success
    
            #step 4
            self.W_ho = self.W_ho + ( self.l_r * np.dot(( self.E_o*self.O_o*(1.0-self.O_o) ),self.O_h.T) )
            self.W_ih = self.W_ih + ( self.l_r * np.dot(( self.E_h*self.O_h*(1.0-self.O_h) ),input_list.T) )
            #step 4 : success
            pass
        def query(self, input_list): 
            self.Sigmoid = lambda x: sig.expit(x)
            #if input_list 의 type이 numpy가 아니라면, 변환하는 과정이 필요하다. 만약 1차원 배열이 input_list라면, np.array(input_list,2).T를 통해 열을 사용하는 numpy array 로 변환해야한다.
            print(input_list.shape)
            self.X_h = np.dot(self.W_ih,input_list)
            self.O_h=self.Sigmoid(self.X_h)
            
            self.X_o = np.dot(self.W_ho, self.O_h)
            self.O_o = sig.expit(self.X_o)
            return self.O_o
    pass
    
    #####
    in_node_num = 3
    hidden_node_num = 3
    out_node_num = 3
    
    learning_rate=0.3
    n6 = neuralNet_v6(in_node_num,hidden_node_num,out_node_num,learning_rate)
    print(n6.query(np.array([1,2,3],ndmin=2).T))
    n6.train(np.array([1,2,3],ndmin=2).T ,np.array([0.7,0.3,0.2],ndmin=2).T)
    print(n6.query(np.array([1,2,3],ndmin=2).T))

     

    반응형

    댓글

Designed by Tistory.