티스토리 뷰

본 포스팅은 『텐서플로2와 머신러닝으로 시작하는 자연어처리』를 참고하여 만들어졌습니다.
https://wikibook.co.kr/nlp-tf2/

 

텐서는 N차원 매트릭스를 의미하며, 말 그대로 텐서를 플로(Flow)한다는 것은 데이터 흐름 그래프(Data flow graph)를 사용해 수치 연산을 하는 과정을 의미한다. 그래프의 노드(Node)는 수치연산(operation), 변수(variable), 상수(constant)를 나타내고 엣지(edge)는 노드 사이를 이동하는 다차원 데이터 배열(tensor)을 나타낸다.

 


1. tf.keras

텐서플로를 이용해 하나의 딥러닝 모델을 만드는 것은 마치 블록을 하나씩 쌓아서 전체 구조를 만들어가는 것과 비슷합니다. 그렇다면 모델을 만들기 위해서는 어떤 블록들이 있는지 알아야 하는데, 텐서플로에는 블록 역할을 할 수 있는 모듈의 종류가 다양하다.

케라스는 텐서플로와 같은 별개의 딥러닝 오픈소스인데, 텐서플로에서도 케라스를 사용 할 수 있다. 텐서플로 2.0 이후 버전에는 기존의 다양한 모듈 중 대부분을 tf.keras.layers의 모듈로 통합하고 이를 표준으로 사용하고 있다. 

 

1) tr.keras.layer.Dense

Dense란 신경망 구조의 가장 기본적인 형태를 의미한다.

 

y = f(Wx + b)

 

Dense는 위 수식을 만적하는 기본적인 신경망 형태의 층을 만드는 함수이다. 이때 x는 입력 벡터, b는 편향 벡터이며 W는 가중치 행렬이 된다. 입력에 가중치를 곱하고 편향을 더한 뒤 f라는 활성화 함수를 적용하는 구조다.

Dense를 이용하지 않고 구현하면 다음과 같다.

W = tf.Variable(tf.random_uniform([5,10],-1.0,1.0))
b = tf.Variable(tf.Zeros([10])

y = tf.manual(W,x) + b

 

Dense를 이용하여 보다 간편하게 구현해보자.

 

Dense를 사용하려면 우선 객체를 생성해야 한다.

input = (20, 1)

# 방법 1 객체 생성 후 다시 호출하여 입력값 설정

dense=tf.keras.layers.Dense(...)
output = dense(input)


# 방법 2 객체 생성 시 입력값 설정

output = tf.keras.layers.Dense(...)(input)

 

Dense 층을 만들 때 여러 인자를 통해 가중치와 편향 초기화 방법, 활성화 함수의 종류 등 여러가지 옵션을 정할 수 있다. 필요에 따라 사용할 인자 값만 지정하면 된다.

__init__(

    units, #출력 값의 크기, Integer 혹은 Long 형태다.
    activation = None, #활성화 함수
    use_bias = True, #편향을 사용할지 여부, Boolean 값 형태다.
    kernel_initializer ='glorot_uniform', #가중치(W) 초기화 함수
    bias_initializer = 'zeros', #편향 초기화 함수
    kernel_regularizer = None, #가중치 정규화 방법
    bias_reqularizer = None, #편향 정규화 방법
    activity_reqularizer = None, #출력 값 정규화 방법
    kernel_constraint = None, #Optimizer에 의해 업데이트된 이후에 가중치에 적용되는 부가적인 제약함수
    bias_constraint=None, #Optimizer에 의해 업데이트된 이후에 편향에 적용되는 부가적인 제약함수
    **kwargs
 
 )

 

(예시1) 입력값에 대해 활성화 함수로 시그모이드 함수를 사용하고 출력 값으로 10개의 값을 출력하는 완전 연결 계층

INPUT_SIZE = (20,1)

inputs = tf.keras.layers.Input(shape=INPUT_SIZE)
output = tf.keras.layers.Dense(units=10, activation = tf.nn.sigmoid)(inputs)

 

(예시2) 10개의 노드를 가지는 은닉층이 있고 최종 출력 값은 2개의 노드가 있는 신경망 구조

INPUT_SIZE = (20,1)

inputs = tf.keras.layers.Input(shape = INPUT_SIZE)
hidden = tf.keras.layers.Dense(units = 10, activation = tf.nn.sigmoid)(inputs)
output = tf.keras.layers.Dense(units = 2, activation = tf.nn.sigmoid)(hidden)

 

2) tf.keras.layers.Dropout

신경망 모델을 만들 때 생기는 여러 문제점 중 대표적인 문제는 Overfitting이다. Overfitting 문제는 정규화(Regularization)방법을 사용해서 해결하는데, 그중 가장 대표적인 방법이 드롭아웃(Dropout)이다. 텐서플로에서는 keras.layers에 입력값데 Dropout을 쉽게 적용 할 수 있도록 모듈을 제공해주고 있다.

 

tf.keras.layers.Dropout( ... )

 

드랍아웃을 적용할 입력값을 설정해야 하는데, 합서 진행했던 것과 입력 값을 설정하는 방법은 동일하다.

 

# 방법1 객체 생성 후 다시 호출하면서 입력값 설정

dropout = tf.keras.layers.Dropout( ... )
output = dropout(input)

# 방법2 객체 생성 시 입력값 설정

output = tf.keras.layers.Dropout( ... )(input)

 

드랍아웃 객체에도 다른 객체와 동일하게 몇 가지 인자를 설정할 수 있다.

 

__init__(
	
    #드롭아웃을 적용할 확률을 지정한다. 확률 값이므로 0~1사이의 값을 받는다.
    rate,
    
    #정수형의 1D-tensor 값을 받는다. 여기서 받은 값은 shape을 뜻하는데,
    #이 값을 지정함으로써 특정 값만 드롭아웃을 적용 할 수 있다.
    noise_shape = None,
    
    #드롭아웃의 경우 지정된 확률 값을 바탕으로 무작위로 드롭아웃을 적용하는데,
    #이때 임의의 선택을 위한 시드 값을 의미한다.
    seed = None,
    
    **kwargs
    
)

 

드롭아웃을 적용하는 과정을 생각해보자. 학습 데이터에 과적합되는 상황을 방지하기 위해 학습 시 특정 확률로 노드의 값을 0으로 만든다. 그리고 이러한 과정은 학습할 때만 적용되고 예측 혹은 테스트할 때는 적용되지 않는다. keras의 dropout을 사용할 경우 이러한 부분이 자동으로 적용된다.

 

(예제1)

INPUT_SIZE=(20,1)

inputs = tf.keras.layers.Input(shape = INPUT_SIZE)
dropout = tf.keras.layers.Dropout(rate = 0.5)(inputs)

 

텐서플로에서 드롭아웃은 tf.nn 모듈에도 있는데 두 모듈의 차이점은 tf.keras.layer.dropout의 경우 확률을 0.2로 지정했을 때 20%를 0으로 만드는 반면, tf.nn.dropout의 경우 확를을 0.2로 지정했을 때 80% 값을 0으로 만든다는 것이다.

 

(예제2) 입력값에 dropout이 적용되고, 10개의 노드를 가지는 은닉층이 있고, 최종 출력값은 2개의 노드가 있는 신경망

 

INPUT_SIZE = (20,1)

input = tf.keras.layers.Input(shape = INPUT_SIZE)
dropout = tf.keras.layers.Dropout(rate=0.2)(input)
hidden = tf.keras.layers.Dense(units=10, activation=tf.nn.sigmoid)(dropout)
output = tf.keras.layers.Dense(units=2, activation=tf.nn.sigmoid)(hidden)

 

3) tf.keras.layers.Conv1D

텐서플로의 합성곱 연산은 Conv1D, Conv2D, Conv3D로 나눠진다.

우리가 흔히 알고 있는 기본적인 이미지에 적용하는 합성곱 방식은 Conv2D이다. 합성곱은 일반적으로 두 가지 기준으로 구분할 수 있다. 바로 합성곱이 진행되는 방향과 합성곱 결과로 나오는 출력값이다.

자연어 처리 분야에서 사용하는 합성곱의 경우 각 단어(혹은 문자) 벡터의 차원 전체에 대해 필터를 적용시키기 위해 주로 Conv1D를 사용한다.

  합성곱의 방향 출력값
Conv1D 가로(한 방향) 1D Array (Vector)
Conv2D 가로, 세로(두 방향) 2D Array(matrix)
Conv3D 가로, 세로, 높이(세 방향) 3D Array(tensor)

 

Conv1D를 사용할 때 앞서 사용했던 Dense와 Dropout과 동일한 방법으로 사용할 수 있다.

# 방법1 객체 생성 후 다시 호출하면서 입력값 설정

Conv1D = tf.keras.layers.Conv1D(...)
output = Conv1D(input)


# 방법2 객체 생성 시 입력값 설정

output = tf.keras.layers.Conv1D(...)(input)

 

합성곱도 필터의 크기, 필터의 개수, 스트라이드 값 등을 객체를 생성할 때 인자로 설정 할 수 있다.

__init__(
	
    #필터의 개수로서 정수형으로 지정한다.
    filters,
    
    #필터의 크기로서 정수 혹은 정수의 리스트, 튜플 형태로 지정한다.
    kernel_size,
    
    #적용할 스트라이드의 값으로서 정수 혹은 정수의 리스트, 튜플 형태로 지정한다.
    #1이 아닌 값을 지정할 경우 dilation_rate는 1 이외의 값을 지정하지 못 한다.
    strides=1,
    
    #패딩 방법을 정한다. "VALID" 또는 "SAME"을 지정할 수 있다.
    padding='valid'
    
    #데이터의 표현 방법을 선택한다. "channel_late", "channel_first"를 지정할 수 있다.
    #channel_last의 경우 데이터는 (batch, length, channels) 형태여야 하고,
    #channel_first의 경우 데이터는 (batch, channels, length) 형태여야 한다.
    data_format='channels_last',
    
    #dilation 합성곱 사용 시 적용할 dilation 값으로서 정수 혹은 정수의 리스트, 튜플 형태로 지정한다. 
    # 1이 아닌 값을 지정하면 stride 값으로 1 이외의 값을 지정하지 못 한다.
    dilation_rate=1,
    
    #활성화 합수
    activation=None,
    
    #편향 사용 여부, Boolean 형태를 갖음
    use_bias=True,
    
    #가중치 초기화 함수
    kernel_initializer='glorot_uniform'
    
    #편향 초기화 함수
    bias_initializer='zeros',
    
    #가중치 정규화 방법
    kernel_regularizer=None,
    
    #편향 정규화 방법
    bias_regularizer=None,
    
    #출력 값 정규화 방법
    activity_regularizer=None,
    
    #Optimizer에 의해 업데이트된 이후 가중치에 적용되는 부가적인 제약함수
    kernel_constraint=None,
    
    #Optimizer에 의해 업데이트된 이후 편향에 적용되는 부가적인 제약함수
    bias_constraint=None,
    **kwargs
)

합성곱은 기본적으로 필터의 크기(kernel_size)를 필요로 하는데, 이 경우 Conv1D는 필터의 높이는 필요하지 않다. Conv1D의 필터는 입력값의 차원 수와 높이가 동일하게 연산되기 때문에 필터의 가로 길이만 설정하면 된다. 그리고 총 몇 개의 필터를 사용할지를 filters 인자를 통해 정해야 한다.

 

(예제1)

INPUT_SIZE = (20,1)

input = tf.keras.layers.Input(shape = INPUT_SIZE)
dropout = tf.keras.layers.Dropout(rate=0.2)(input)
hidden = tf.keras.layers.Dense(units=10, activation=tf.nn.sigmoid)(dropout)
output = tf.keras.layers.Dense(units=2, activation=tf.nn.sigmoid)(hidden)

 

(예제2) 입력값에 드롭아웃을 적용한 합성곱 신경망도 Dropout과 Conv1D를 사용해서 구현할 수 있다.

INPUT_SIZE = (1,28,28)

input = tf.keras.Input(shape=INPUT_SIZE)
dropout = tf.keras.layers.Dropout(rate=0.2)(input)
conv = tf.keras.Conv1D(
	filters=10,
    kernel_size=3,
    padding='same',
    activation=tf.nn.relu)(dropout)

 

4) tf.keras.layers.MaxPool1D

합성곱 신경망과 함께 쓰이는 기법 중 하나는 풀링이다. 보통 피처 맴 크기를 줄이거나 주요한 특징을 뽑아내기 위해 합성곱 이후 적용되는 기법이다. 불링에는 주로 두 가지 풀링 기법이 사용되는데, 맥스 풀링(Max-pooling)과 평균 풀링(average-pooling)이 있다. 

맥스 풀링도 합성곱과 같이 세 가지 형태로 모델이 구분돼 있다. MaxPool1D, MaxPool2D, MaxPool3D로 나눠져 있는데 합성곱과 똑같은 원리다. 자연어 처리에 주로 사용되는 합성곱과 동일하게 MaxPool1D를 주로 사용하는데, 한 방향으로만 풀링이 진행된다. 

 

# 방법1 객체 생성 후 apply 함수를 이용해 입력값 설정

max_pool = tf.keras.layers.MaxPool1D(...)
max_pool.apply(input)


# 방법2 객체 생성 시 입력값 설정

max_pool = tf.keras.layers.MaxPool1D(...)(input)

 

맥스 풀링도 여러 인자 값을 설정함으로 풀링이 적용되는 필터의 크기 등을 설정할 수 있다.

 

__init__(
	
    #풀링을 적용할 필터의 크기를 뜻한다. 정수값을 받는다.
    pool_size=2,
    
    #적용할 스트라이드의 값, 정수 혹은 None값을 받는다.
    strides=None,
    
    #패딩 방법을 지정한다. "valid" 또는 "same"을 지정할 수 있다.
    padding='valid',
    
    #데이터의 표현 방법을 선택한다. "channel_last" 혹은 "channel_last"를 지정 할 수 있다.
    #channel_last의 경우 데이터(batch, length, channels) 형태여야 하고,
    #channel_first의 경우 데이터는(batch, channels, length) 형태여야 한다.
    data_format=None,
    
    **kwargs
)

 

(예제1) 입력값이 합성곱과 맥스 풀링을 사용한 후 완전 연결 계층을 통해 최종 출력 값이 나오는 구조

맥스풀링 결과값을 완전 연결 계층으로 연결하기 위해서 행렬을 벡터로 만들어야 한다. 이를 위하여 tf.keras.layer.Flatten을 사용한다.

 

INPUT_SIZE = (1,28,28)

inputs = tf.keras.Input(shape = INPUT_SIZE)
dropout = tf.keras.layers.Dropout(rate=0.2)(inputs)
conv=tf.keras.Conv1D(
	filters=10,
    kernel_size=3,
    padding='same',
    activation=tf.nn.relu)(input)
    
max_pool = tf.keras.layers.MaxPool1D(pool_size=3, padding='same')(conv)

flatten = tf.keras.layers.Flatten()(max_pool)

hidden = tf.keras.layers.Dense(units=50, activation = tf.nn.relu)(flatten)

output = tf.keras.layers.Dense(units=10, activation = tf.nn.softmax)(hidden)

2. TensroFlow 2.0

텐서플로 2.0을 활용해서 모델을 구축하고 학습하는 효율적인 방법을 알아보자.

 

1)  API 정리

기존 텐서플로 1.x 버전에서는 같은 기능을 수행하는 다양한 API가 다양한 패키지에 속해있었다. 그 뿐만 아니라 복잡하게 흩어져 있던 다양한 API들을 모두 파악하고 사용하기가 매우 어려웠다. 텐서플로 2.0에서는 명료하고 사용하기 편하도록 동일한 기능의 다양한 API를 하나로 통합하고 잘 사용되지 않은 API를 제거하였다.

 

2) 이거모드 (Eager excution)

텐서플로의 즉시 실행은 그래프를 생성하지 않고 함수를 바로 실행하는 명령형 프로그래밍 환경입니다. 나중에 실행하기 위해 계산가능한 그래프를 생성하는 대신에 계산값을 즉시 알려주는 연산입니다. 이러한 기능은 텐서플로를 시작하고 모델을 디버깅하는 것을 더욱 쉽게 만들고 불필요한 상용구 코드(boilerplate code) 작성을 줄여줍니다.

 

3) 모델 구축

텐서플로 2.0에서는 케라스를 활용해 모델을 구축하고 학습하는 것을 권장한다. 케라스 API는 고수준 API로서 사용하기 간편할뿐더러 매우 유연하고 높은 성능을 보여준다. 케라스를 활용해 모델을 구죽하는 방법은 다음과 같은 방법으로 분류 할 수 있다.

 

  • Sequential API
  • Functional API
  • Functional/Sequential API + Cuntom Layers
  • Subclassing (Custom Model)

 

(1) Sequential API

tf.keras.Sequential은 케라스를 활용해 모델을 구축할 수 있는 가장 간단한 형태의 API이다. Sequential 모듈을 이용하면 간단한 순차적인 레이어의 스택을 구현할 수 있다.

 

(예제)

from tensorflow.keras import layers

model = tf.keras.Sequential()
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(64.activation='relu'))
model.add(layers.Dense(10,activation='softmax'))

 

Sequential 인스턴스를 생성한 후 해당 인스턴스에 여러 레어이를 순차적으로 더하기만 하면 모델이 완성된다. Sequential 모듈의 경우 위와 같이 구현 자체가 매우 간단하지만, 모델의 층들이 순차적으로 구성돼 있지 않은 경우에는 Sequential 모듈을 사용해 구횬하기가 어려울 수 있다.

 

(2) Functional API 

 

앞에서 살펴본 Sequential 모듈은 간단한 레이어들의 스택 구조에는 적합하지만 복잡한 모델의 경우에는 구현상의 제약이 있을 수 있다. 가령 모델의 구조가 다음과 같은 경우 Sequential 모듈을 사용하기가 어려울 수 있다.

  • 다중 입력값 모델(Multi-input models)
  • 다중 출력값 모델(Multi-output models)
  • 공유 층을 활용하는 모델(Models with shared layers)
  • 데이터 흐름이 순차적이지 않은 모델(Models with non-sequential data flows

이러한 모델을 구현할 때는 케라스의 Functional API를 사용하거나 이후 살펴볼 Subclassing 방식을 사용하는 것이 적절할 수 있다.

 

(예제)

input = tf.keras.Input(shape=(32,))
x=tf.keras.layers.Dense(64, activation='relu')(input)
x=tf.keras.layers.Dense(64,activation='relu')(x)
predictions=tf.keras.layers.Dense(10,activation='softmax')(x)

 

(3) Custom Layer

새로운 연산을 하는 레이어 혹은 편의를 위해 여러 레이어를 하나로 묶은 레이어를 구현해야 하는 경우가 있다. 이때 Custom layer을 만들어 사용하면 된다.

 

(예제1)

class CustomLayer(tf.keras.layers.Layer):
	
    def __init__(self, hidden_dimension, hidden_dimension2, output_dimension):
    	self.hidden_dimension = hidden_dimension
        self.hidden_dimension2 = hidden_dimension2
        self.output_dimension = output_dimension
        super(CustomLayer, self).__init__()
        
    def build(self, input_shape):
        self.dense_layer1 = tf.keras.layers.Dense(self.hidden_dimension, activation='relu')
        self.dense_layer2 = tf.keras.layers.Dense(self.hidden_dimension2, activation='relu')
        self.dense_layer3 = tf.keras.layers.Dense(self.output_dimension, activation='softmax')
        
    def call(self, inputs):
    	x=self.dense_layer1(input)
        x=self.dense_layer2(x)
        
        return self.dense_layer3(x)
        
        
# __main__

import tensorflow as tf

model = tf.keras.Sequential()
model.add(CustomLayer(64,64,10))

(4) Subclassing (Custom Model)

자유도가 가장 높은 Subclassing을 알아보자. 

이 경우 tf.keras.Model을 상속받고 모델 내부 연산들을 직접 구현하면 된다. 모델 클래스를 구현할 때는 객체를 생성할 때 호출되는 __init__ 매서드와 생성된 인스턴스를 호출할 때 (모델 연산이 사용될 때) 호출되는 call 메서드만 구현하면 된다. __init__ 메서드에서는 모델에 사용될 층과 변수를 정의하면 되고, call 메서드에서는 이렇게 정의한 내용을 활용해 모델 연산을 진행하면 된다. 참고로 모델에서 사용될 층을 정의할 때도 Custom layer을 사용 할 수 있다.

 

(예제1)

class MyModel(tf.keras.Model):

	def __init__(self, hidden_dimension, hidden_dimension2, output_dimension):
    	super(MyModel, self).__init__(name='my model')
        
        self.dense_layer1 = tf.keras.layers.Dense(hidden_dimension, activation='relu')
        self.dense_layer2 = tf.keras.layers.Dense(hidden_dimension2, activation='relu')
        self.dense_layer3 = tf.keras.layers.Dense(output_dimension, activation='softmax')
        
    def call(self, inputs):
    	x = self.dense_layer1(input)
        x = self.dens_layer2(x)
        
        return self.dense_layer3(x)

 

4) 모델 학습

모델 학습의 방법은 실제로는 학습뿐 아니라 모델 검증, 예측 등 여러 과정을 포함한다는 점을 알아두자. 텐서플로 2.0 공식 가이드에서 모델 학습에 대해 권장하는 방법은 크게 두가지로 나뉜다.

  1. 케라스 모델의 내장 API를 활용하는 방법 (예시, model.fit( ), model.evaluate( ), model.predict( ) )
  2. 학습, 검증, 예측 등 모든 과정을 GradientTape 객체를 활용해 직접 구현하는 방법 

첫 번째 방법의 경우 대부분 케라스 모델의 메서드로 이미 구현돼 있어 간편하다는 큰 장점이 있고, 두 번째 방법의 경우 첫 번째 방법과 비교했을 때 일일이 구현해야 한다는 단점이 있지만 좀 더 복잡한 로직을 유연하고 자유롭게 구현할 수 있다는 장점이 있다.

 

(1) 내장 API를 활용하는 방법

이미 정의된 케라스의 모델 객체가 있다고 가정해보자. 이 모델 객체는 케라스의 모델 객체이기 때문에 여러 메서드가 이미 내장돼 있다. 따라서 내장 메서드를 간단하게 사용하기만 하면 된다. 먼저 해야 할 일은 학습 과정을 정의하는 것이다. 즉, 학습 과정에서 사용될 손실 함수, 옵티마이저, 평가에 사용될 지표 등을 정의하면 된다.

 

# 방법1

model.compile(optimizer = tf.keras.optimizers.Adam(),
	      loss=tf.keras.losses.CategoricalCrossentropy(),
              metrics=[tf.keras.metrics.Accuracy()])
              
# 방법2

model.compile(optimizer = 'adam',
	      loss='categrical_crossentropy',
              metrics=['accuracy'])

 

이제 정의된 모델 객체를 대상으로 학습, 평가, 예측 메서드를 호출하면 정의한 값들을 활용하여 학습이 진행된다. 즉, 다음과 같이 fit 메서드를 호출하면 데이터들이 모델을 통과하면서 학습이 진행된다. 아울러 학습이 진행되면서 각 에폭당 모델의 성능(손실함수, 정확도) 등이 출력되는 것을 확인할 수 있다.

model.fit(x_train, y_train, batch_size=64, epochs=3, validation_date=(x_val, y_val))
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함