[딥.한.끝] 3. 딥러닝 구조와 모델

PurinYun·2023년 12월 18일
0

AIffel

목록 보기
57/75

1. 딥러닝 구조

모델 구조

케라스에서 제공하는 API들의 구성도를 살펴보면, 크게 Model API와 Layer API가 있고 필요한 모듈들을 Modules API를 호출해서 사용합니다. 딥러닝 모델은 여러 레이어들로 구성되어 있습니다.

2. 레이어(Layer)

먼저 딥러닝을 사용하는데 필요한 라이브러리인 TensorFlow와 keras를 임포트합니다. 또한 케라스(Keras)의 레이어를 사용할 수 있도록 임포트합니다.

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

딥러닝은 여러 개의 레이어(Layer)로 구성되어 있으며, 기본적으로 입력층(Input Layer), 은닉층(Hidden Layer), 출력층(Output Layer) 으로 구분됩니다. 레이어는 딥러닝 모델을 구성하는 핵심 데이터 구조로서 하나 이상의 텐서를 입력받아 하나 이상의 텐서를 출력하는 데이터 처리 모듈입니다.

2-1. Input 객체

딥러닝 모델의 입력을 정의할 때 사용하는 Input 객체는 입력 데이터의 모양인 shape와 예상되는 데이터 유형인 dtype 등을 포함하고 있습니다.

keras.Input(shape=(8,), dtype=tf.int32)

<KerasTensor: shape=(None, 8) dtype=int32 (created by layer 'input_1')>

Input 객체는 배치 크기를 batch_size로 지정할 수 있고, name을 통해서 이름을 지정할 수도 있습니다.

keras.Input(shape=(28, 28), dtype=tf.float32, batch_size=16)

<KerasTensor: shape=(16, 28, 28) dtype=float32 (created by layer 'input_3')>

2-2. Dense 레이어

Dense 레이어는 완전연결계층(Fully-Connected Layer)으로 노드수(유닛수)를 지정하면 생성됩니다. 레이어에 name을 통해 이름을 지정하여 레이어간의 구분이 가능합니다.

layers.Dense(10, name='layer1')

<keras.layers.core.Dense at 0x7f54d750af70>

레이어에서 사용할 활성화 함수(activation)를 지정하여 생성하는 것이 가능합니다.

layers.Dense(10, activation='softmax')

<keras.layers.core.Dense at 0x7f54d7517880>

레이어에 유닛수와 활성화 함수 그리고 이름까지 지정하여 사용하는 것도 가능합니다.

layers.Dense(10, activation='relu', name='Dense Layer')

랜덤으로 값을 만들고, 생성한 레이어에 그 값을 입력으로 사용한 뒤에 레이어 가중치 값과 결과 값을 확인해 봅시다.

inputs = tf.random.uniform(shape=(5, 2))
print(inputs)
layer = layers.Dense(10, activation='relu')
outputs = layer(inputs)
print(layer.weights)
print(layer.bias)
print(outputs)

2-3. Activation 레이어

활성화 함수는 이전 레이어의 결과값을 변환하여 다른 레이어로 전달하는 역할을 하는데, 크게 선형 활성화 함수(Linear activation function)와 비선형 활성화 함수(Non-linear activation function)로 구분할 수 있습니다. 다만 딥러닝에서는 선형 활성화 함수를 사용하면 다층 구조를 사용하는 의미가 없기 때문에 모델 표현력을 위해서 비선형 활성화 함수 를 사용합니다.

케라스의 Dense 레이어에서 미리 활성화 함수를 지정할 수도 있지만, 필요에 따라 별도의 Activation 레이어를 만들어줄 수 있습니다.

Activation 레이어가 어떤식으로 동작하는지 살펴보기 위해서 -10부터 10 사이의 수 가운데 100개의 값을 입력 데이터로 사용해 봅시다.

import numpy as np
import matplotlib.pyplot as plt

input = np.linspace(-10, 10, 100)
x = np.linspace(-10, 10, 100)
plt.scatter(x, input)
plt.show()

시그모이드 함수

활성화 함수 중에서도 오래전부터 사용되던 시그모이드(sigmoid) 함수는 다음과 같이 표현됩니다.

시그모이드 함수는 주로 확률 예측 모델에 자주 사용되며, 출력값이 0과 1 사이로 나타납니다. 그러다보니 0과 1 사이에서 포화(saturate)되는 문제가 있습니다. 즉, 입력값이 작아도 출력값이 0 이하가 될 수 없고, 커져도 1 이상이 될 수 없기 때문에 훈련 시간이 오래걸리고, 그래디언트(gradient)가 0에 가까워져서 가중치 업데이트가 발생하지 않게 됩니다.

layer = layers.Activation('sigmoid')
output = layer(input)

plt.scatter(x, output)
plt.show()

하이퍼볼릭 탄젠트 함수

하이퍼볼릭 탄젠트(tanh, Hyperbolic tangent) 함수는 다음과 같이 표현됩니다.

하이퍼볼릭 탄젠트 함수는 0을 중심으로 -1과 1 사이의 값이 나타납니다. 일반적으로 시그모이드 함수보다는 빨리 훈련되지만, 여전히 -1과 1 사이에서 포화되는 문제가 있습니다.

layer = layers.Activation('tanh')
output = layer(input)

plt.scatter(x, output)
plt.show()

ReLU 함수

최근에 가장 많이 사용하는 활성화 함수인 ReLU(Rectified Linear Unit)는 다음과 같이 표현됩니다.

ReLU 함수의 출력값은 0부터 무한대까지의 범위의 값을 가지므로 하이퍼볼릭 탄젠트 함수보다 몇 배는 더 빠르게 훈련됩니다. 다만 ReLU 함수 출력값이 0이 중심이 아니고, 학습률(learning rate)이 크면 ReLU를 사용한 노드에서 출력이 0으로만 나온다는 문제가 있습니다.

layer = layers.Activation('relu')
output = layer(input)

plt.scatter(x, output)
plt.show()

Leaky ReLU 함수

Leaky ReLU 함수는 ReLU 함수의 문제점, 즉 노드 출력을 0으로만 하던 문제를 해결하였으며 다음과 같이 표현됩니다.

Leaky ReLU 함수는 노드 출력을 0이 아닌 아주 작은 음수값으로 출력하게 만들어 해결하였습니다.

layer = layers.LeakyReLU()
output = layer(input)

plt.scatter(x, output)
plt.show()

ELU 함수

ELU(Exponential Linear Unit) 함수는 ReLU가 중심점이 0이 아니고, 노드 출력을 0으로만 하던 문제를 해결한 활성화 함수이며, 다음과 같이 표현됩니다.

ELU 함수는 0 이하에서는 exponential 연산이 수행되므로 계산 비용이 높아지는 단점이 있습니다.

layer = layers.ELU()
output = layer(input)

plt.scatter(x, output)
plt.show()

2-4. Flatten 레이어

Flatten 레이어는 배치 크기(또는 데이터 크기)를 제외하고 데이터를 1차원 형태로 평평하게 변환합니다.

inputs = keras.Input(shape=(28, 28, 1))
layer = layers.Flatten(input_shape=(28, 28, 1))(inputs)
print(layer.shape)

(None, 784)

Q.입력값 (224, 224, 1)를 Flatten 레이어에 넣으면 어떤 크기의 1차원 형태 데이터가 나올까요?

inputs = keras.Input(shape=(224, 224, 1))
layer = layers.Flatten()(inputs)
print(layer.shape)

(None, 50176)

3. 딥러닝 모델

먼저 케라스(Keras)에서 모델과 유틸리티들을 사용할 수 있도록 models, utils를 임포트하겠습니다.

from tensorflow.keras import models, utils

3-1. Sequential API

Sequential API를 이용하는 방법은 모델이 순차적인 구조로 진행할 때 사용하는 간단한 방법입니다. 다만 이 방법은 다중 입력 및 출력이 존재하는 등의 복잡한 모델을 구성할 수 없습니다.

Sequential API를 이용한 방법 중 첫번째는 Sequential 객체 생성 후,add()를 이용하여 사용할 레이어들을 추가하는 방법입니다.

model = models.Sequential()
model.add(layers.Flatten())
model.add(layers.Input(shape=(28, 28)))
model.add(layers.Dense(300, activation='relu'))
model.add(layers.Dense(100, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))
model.summary()

plot_model() 함수를 통해 딥러닝 모델의 모습을 시각적으로 확인할 수 있습니다.

utils.plot_model(model)

다른 방법으로는 Sequential 인자에 한번에 추가하는 방법이 있습니다.

model = models.Sequential([layers.Input(shape=(28, 28), name='Input'),
                           layers.Dense(300, activation='relu', name='Dense1'),
                           layers.Dense(100, activation='relu', name='Dense2'),
                           layers.Dense(10, activation='softmax', name='Output')])
model.summary()

utils.plot_model(model)

3-2. Functional API

모델 생성에 Functional API를 이용하는 방법은 가장 권장되는 방법입니다. Functional API는 딥러닝 모델을 복잡하고, 유연하게 구성하는 것이 가능하며, 다중 입출력을 다룰 수 있습니다.

inputs = layers.Input(shape=(28, 28, 1))
x = layers.Flatten(input_shape=(28, 28, 1))(inputs)
x = layers.Dense(300, activation='relu')(x)
x = layers.Dense(100, activation='relu')(x)
x = layers.Dense(10, activation='softmax')(x)

model = models.Model(inputs=inputs, outputs=x)
model.summary()

utils.plot_model(model)

Functional API를 이용하면 Input 객체를 여러 레이어에서 사용하는 것이 가능합니다. 아래의 예제에서는 Concatenate()를 이용하여 Dense 레이어 결과와 Input을 결합하였습니다.

inputs = keras.Input(shape=(28, 28))
hidden1 = layers.Dense(100, activation='relu')(inputs)
hidden2 = layers.Dense(30, activation='relu')(hidden1)
concat = layers.Concatenate()([inputs, hidden2])
output = layers.Dense(1)(concat)

model = models.Model(inputs=[inputs], outputs=[output])
model.summary()

utils.plot_model(model)

Functional API를 이용하면 여러 Input 객체를 사용하는 것도 가능합니다.

input_1 = keras.Input(shape=(10, 10), name='Input_1')
input_2 = keras.Input(shape=(10, 28), name='Input_2')

hidden1 = layers.Dense(100, activation='relu')(input_2)
hidden2 = layers.Dense(10, activation='relu')(hidden1)
concat = layers.Concatenate()([input_1, hidden2])
output = layers.Dense(1, activation='sigmoid', name='output')(concat)

model = models.Model(inputs=[input_1, input_2], outputs=[output])
model.summary()

utils.plot_model(model)

Functional API를 이용하면 결과를 여러개로 나눠서 사용하는 것도 가능합니다.

input_ = keras.Input(shape=(10, 10), name='input_')
hidden1 = layers.Dense(100, activation='relu')(input_)
hidden2 = layers.Dense(10, activation='relu')(hidden1)
output = layers.Dense(1, activation='sigmoid', name='main_output')(hidden2)
sub_out = layers.Dense(1, name='sum_output')(hidden2)

model = models.Model(inputs=[input_], outputs=[output, sub_out])
model.summary()

utils.plot_model(model)

Functional API를 이용하여 다중 입력과 다중 출력이 가능한 모델도 만들 수 있습니다.

input_1 = keras.Input(shape=(10, 10), name='input_1')
input_2 = keras.Input(shape=(10, 28), name='input_2')
hidden1 = layers.Dense(100, activation='relu')(input_2)
hidden2 = layers.Dense(10, activation='relu')(hidden1)
concat = layers.Concatenate()([input_1, hidden2])
output = layers.Dense(1, activation='sigmoid', name='main_output')(concat)
sub_out = layers.Dense(1, name='sum_output')(hidden2)

model = models.Model(inputs=[input_1, input_2], outputs=[output, sub_out])
model.summary()

utils.plot_model(model)

3-3. Subclassing API

서브클래싱(Subclassing) 방법은 커스터마이징에 최적화된 방법으로, Model 클래스를 상속받아서 사용하고 모델에 포함되는 다음과 같은 기능을 사용할 수 있습니다.

fit(): 모델 학습
evaluate(): 모델 평가
predict(): 모델 예측
save(): 모델 저장
load(): 모델 불러오기
call(): 메소드안에서 원하는 계산 가능

Subclassing API를 사용하면 Functional API로도 구현할 수 없는 모델들도 구현이 가능하지만, 객체지향 프로그래밍(Object-oriented Programming)에 익숙해야 합니다.

# Subclassing API의 예시
class MyModel(models.Model):
  def __init__(self, units=30, activation='relu', **kwargs):
    super(MyModel, self).__init__(**kwargs)
    self.dense_layer1 = layers.Dense(300, activation=activation)
    self.dense_layer2 = layers.Dense(100, activation=activation)
    self.dense_layer3 = layers.Dense(units, activation=activation)
    self.output_layer = layers.Dense(10, activation='softmax')

  def call(self, inputs):
    x = self.dense_layer1(inputs)
    x = self.dense_layer2(x)
    x = self.dense_layer3(x)
    x = self.output_layer(x)
    return x

profile
Fantivation

0개의 댓글