Data_study/Deep Learning

[Deep_learning] Dense Layers 구현

onsemiro 2022. 2. 4. 15:14

<구현 과정>

dense layers 진행과정과 진행하면서 변화되는 입력데이터의 shape와 weight, bias의 shape도 확인해보자.
1. x입력데이터는 tf.random.normal로 shape를 설정하여 만들자.
2. dense 패키지를 활용하여 affine과 activation을 동시에 연산을 진행하는 객체를 만들고 거기에 x입력값을 넣자.
3. 연산 다 된 상태에서 이제 W, B와 마지막으로 나온 출력값의 shape들을 확인해보자.

 

 

Dense Layers

우선 Layer가 하나 있는 형태를 통해 각 파라미터와 출력값의 shape를 확인해보자.

 

필요한 패키지들을 불러오고, 입력데이터를 만들자.

import tensorflow as tf
from tensorflow.keras.layers import Dense

N, n_feature = 4,10 #N은 데이터의 행개수, n_feature은 특성 즉 열의 개수
X = tf.random.normal(shape=(N,n_feature))

 

 

tf.randam.normal로 만들어진 X 입력데이터를 layer에 입력해보자.

neuron의 개수는 3개로 정하였고 activation function은 sigmoid로 설정하여 진행하였다.

n_neuron = 3
dense = Dense(units = n_neuron, activation = "sigmoid")
Y_tf = dense(X)

 

진행된 dense 객체에서 Weight값과 bias값을 꺼내어 shape를 확인해보자.

그리고 마지막에는 실제 출력값이 어떻게 나오는지도 보자.

W, B = dense.get_weights()

print('==== Input/Weight/Bias ====')
print('X: ', X.shape)
print('W: ', W.shape)
print('B: ', B.shape)
print('Y: ', Y_tf.shape)
print(Y_tf)

 

그 결과..

Shapes of Input/Weight/bias

이 구현을 통해 다시한번 확인한 점.

  • 입력 데이터 X의 행은 그대로 출력값의 행개수로 형성된다.
  • 입력 데이터 X에 맞게 W의 행개수가 할당되어 연산 된다.
  • neuron의 개수가 곧, W의 열개수이고 B의 행개수다.
  • 출력값의 shape는 (X의 행개수, 마지막 layer의 neuron개수)다.

 

Calculate with dot products

위에서 구현할 때 사용한 Dense 매소드가 어떤 과정을 거쳤는지 실제 연산으로 확인해보자.

 

우선 필요한 패키지인 np와 exp, matmul를 불러온다.

그 후, np.zeros를 활용하여, shape=()형태에 맞게 값 0 으로 구성된 하나의 matrix를 만든다.

import numpy as np
from tensorflow.math import exp
from tensorflow.linalg import matmul

#calculate with dot products - dot product형태로 곱을하여 Yo_man_vec에 모두 모은 것.
Y_man_vec = np.zeros(shape=(N, n_neuron))

 

아래 코드는 X에서 한 행을 뽑은 vector과 Weight에서 뽑은 vector를 dot product한 후, bias에서 뽑은 원소와 더하는 과정이다.

for x_idx in range(N):
    x = X[x_idx]
    
    for nu_idx in range(n_neuron):
        w, b = W[:,nu_idx],B[nu_idx]
        z = tf.reduce_sum(x *w)+b
        
        a = 1/(1+exp(-z))
        Y_man_vec[x_idx,nu_idx] = a #Y_man_vec metrix에 a값을 입력시켜준다. 행위치는 x_idx, 열위치는 nu_idx다.
        
print("Y(with dot products): \n", Y_man_vec)

 

그 결과..

calculate with dot products

Dense 패키지로 진행하여 나온 출력값과 직접하나하나 dot products를 한 출력값과 일치함을 알 수 있다.

이로써, Dense 패키지가 이떻게 진행되는지를 확인하였다.

 

 

Cascaded Dense Layers

이번에는 Cascaded Dense Layers를 구현해보자.

 

 

우선 이번에도 필요한 패키지를 불러오고 입력데이터 X도 만들어보자.

Cascaded Dense Layers이므로, neurons 값은 1개 이상이어야한다. 그래서 리스트로 생성해봤다.

import tensorflow as tf
from tensorflow.keras.layers import Dense

N, n_features =4, 10
X = tf.random.normal(shape=(N,n_feature))
print("input:", X.shape)

n_neurons = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]

 

 

이제 남은 것은 Cascaded dense 연산을 진행하는 것이다.

첫번째 layer에서 출력된 값을 다음 layer로 전달해주고 그렇게 마지막 layer까지 연산을 진행하는 과정이다.

 

 

이 과정에는 반복되는 일이 생기기 때문에, for문을 활용하자.

우선, 각 layer들을 모두 하나의 list에 넣고나서, 하나씩 꺼내면서 연산을 이어가보자.

 

dense_layers = list() #list에 dense자체를 넣는 것이다.
for n_neuron in n_neurons:
    dense = Dense(units = n_neuron, activation = "relu")
    dense_layers.append(dense)

for dense_idx, dense in enumerate(dense_layers):
#enumerate는 for문에서 활용하는데, (list)안에 있는 것을 (idex,원소)로 형성하여 tuble형태로 만들어준다.
    X = dense(X) #matrix형태의 X자체를 dense에 넣어 바로 연산을 진행한다.
    print("After dense layer", dense_idx)
    print(X.shape, '\n') #첫번째 layer에 들어갔다가 연산된 X를 출력한다.
Y = X #마지막 layer에서 나온 X값은 결괏값이므로, Y로 지정해준다.

 

그 결과..

Shapes of Cacaded Dense Layers

위 사진을 통해 알 수 있는 것.

  • -처음 입력된 X의 N값은 계속해서 행의 수로 지속되고, 쌓인 layer에 들어갔다 나오면, layer의 neuron 개수로 열의 수가 변한다.

이유는, X(4x10) * W(10xneuron개수) -> X(4xneuron개수) 행렬의 곱을 생각해보면 이해할 수 있다.

 

 

Model_Implementation

 

mini batch를 실행하면, 동일한 모델에 분할된 데이터 세트들이 순차적으로 들어갈 때가 많을 것이다.

이런 경우에는 모델과정을 하나의 class객체로 묶어서 정의한 후에 추후 필요한 경우 불러오는 게 훨씬 편리하다.

 

 

class객체로 정의하는 건 두가지 유형이 있다.

  • 유형 1. class 객체 내에 list를 만들어, dense 패키지를 모두 삽입한 후에 하나씩 꺼내어 순차적으로 연산한다.
  • 유형 2. class 객체 내에서 sequential 객체를 만들어서 dense 패키지를 삽입한 후에 x를 넣어 연산을 실행후 x로 변환하게 한다.

 

유형 1. class 객체 내에 list를 만들어, dense 패키지를 모두 삽입한 후에 하나씩 꺼내어 순차적으로 연산한다.

 

우선 필요한 패키지와 x 입력 데이터를 정의하자.

import tensorflow as tf
from tensorflow.keras.layers import Dense

from tensorflow.keras.models import Sequential
from tensorflow.keras.models import Model

x = tf.random.normal(shape=(4,10))

 

TestModel_1라는 class 객체를 만들자.

class TestModel_1(Model):
    def __init__(self, n_neurons):
        super(TestModel_1,self).__init__()
        self.n_neurons = n_neurons  #밖에서 불러온 n_neurons을 가져와서 내부 변수로 만들어준다.
        
        self.dense_layers = list() #dense들을 넣을 list하나를 만든다.
        for n_neuron in self.n_neurons:
            self.dense_layers.append(Dense(units=n_neuron,activation = 'sigmoid')) #각 n_neurons 리스트에 있었던 neurons개수들을 units에 넣어서 dense 연산을 생성한다.
            
    def call(self,x):  #list는 아래와 같이 list안에 있는 layer를 하나씩 꺼내가면서 입력값을 넣고 연산해줘야한다.
        for dense in self.dense_layers:
            x = dense(x) #dense_layers에 들어가 있는 layer들을 순차적으로 통과하면서 연산되어 나간다.
        return x # 그후 마지막 X값이 변환된다.

n_neurons = [10,20]
model_1 = TestModel_1(n_neurons)
Y_subclassing_1= model_1(x)
print("----------------------------Y_subclassing_1\n",Y_subclassing_1.numpy())

위 코드는 dense layer를 구현할 때, 사용한 과정으로

 

denes 매소드 자체를 list에 넣어, 하나의 list를 형성한다.

그 후, dense_layers라는 list에서 하나씩 dense 매소드를 꺼내어, x 데이터 세트를 연산하고, 연산 된 출력값을 다시 x로 정의한다.

계속해서 반복진행하면, 마지막의 x 데이터는 마지막 layer에서 나온 출력값일 것이다. 이 x값을 class의 반환값으로 return 해주면, 모델의 마지막 결괏값이 도출될 것이다.

 

결과는 아래와 같다.

Model_Implementation - classing list

 

유형 2. class 객체 내에서 sequential 객체를 만들어서 dense 패키지를 삽입한 후에 x를 넣어 연산을 실행후 x로 변환하게 한다.

 

필요한 패키지와 x데이터를 형성하는 코드는 위와 같으므로 생략한다.

 

 

TestModel_2라는 class 객체를 만들자.

 

class TestModel_2(Model):
    def __init__(self):
        super(TestModel_2,self).__init__()
        
        #sequential method
        self.model = Sequential()
        self.model.add(Dense(units=10,activation = 'sigmoid'))
        self.model.add(Dense(units=20,activation = 'sigmoid'))
        
    def call(self,x):
        x = self.model(x)
        return x # 그후 마지막 X값이 변환된다.
model_2 = TestModel_2()
Y_subclassing_2= model_2(x)
print("----------------------------Y_subclassing_2\n",Y_subclassing_2.numpy())

위 코드는 list 대신, sequential method를 활용한 것이다.

 

코드 구현의 큰 틀은 위 list와 동일하다. 하지만, sequential은 list와 달리 들어가있는 layer를 하나씩 꺼내어 순차적으로 연산을 진해해주는 기능이 있다. 그렇기 때문에, for문을 통해 layer를 하나씩 추출하는 코드 구현은 필요없다.

 

 

<sequential method>

sequential이란 "순차적인"의 뜻을 갖고 있는 것처럼 각 dense layer들을 넣으면, 순차적으로 연산을 진행해주는 역할을 한다.

 

>print(model.layers)를 해보면, model.layers에 리스트 형태로 dense layer가 들어있음을 알 수 있다.

>model.summary()를 실행하면, 아래와 같이 dense들이 layer에 들어가있고 output shape도 볼 수 있다. 파라미터 개수도 입력되있는 것을 볼 수 있다.

model.summary()

 

 

위 코드 구현의 결과는 아래와 같다.

Model_Implementation - classing squential method

 

list로 dense layer를 모아서 for문을 통해 연산한 것과

sequential method를 통해 연산하여 나온 출력값이 동일함을 알 수 있다.

 

 

마무리

코드 구현은 항상 느끼는 거지만, 좋은 패키지가 있다고 불러와서 사용하여 좋은 결과를 도출해내는 것이 다가아니다. 내가 패키지를 불러와 활용을 한다고 해도, 그 패키지가 어떤 과정으로 진행되고 있는지 확인하고, 구현전에 배운 이론의 개념이 어떻게 코드로 구현되었는지 항상 확인해봐야한다. 명심하고 귀찮다고 생각하지말고 가장 중요하고 필요한 것이라고 생각하자.

반응형