카테고리 없음

케라스 창시자에게 배우는 딥러닝 7장

찐에이아이 2023. 2. 3. 13:39

케라스에서 모델을 만드는 3가지 API

 

  1. Sequential 모델: 기본적으로 하나의 파이썬 리스트, 단순히 층을 쌓을 수만 있음
  2. 함수형 API: 가장 널리 사용되는 모델 구축 API
  3. Model 서브클래싱: 모든 상세한 내용을 완전히 제어하고 싶을 경우에 적합.  케라스 내장 기능을 사용하지 못하므로 디버깅하는데 어려움이 있을 가능성이 큼

 

Sequential 모델:

 

기본적으로 하나의 파이썬 리스트이다.

 

기본 sequential 모델

from tensorflow import keras
from tensorflow.keras import layers

model = keras.Sequential([
	layers.Dense(64,activation = "relu"),
    layers.Dense(10, activation = "softmax")
])

위 코딩에는 입력의 크기가 주어지지 않았다. 따라서 가중치를 만들 수 없다.

 

가중치를 생성하기 위해서는 어떤 데이터로 호출하거나 입력 크기를 지정하여 build() 메서드를 호출해야 한다.

 

model.build(input_shape = (None, 3))#입력의 크기가 None은 어떤 배치 크기도 가능하다는 뜻이다
model.weights

 

sequential 모델의 다른 표현법

 

model = keras.Sequential(name = "my_example_model")
model.add(layers.Dense(64, activation = "relu", name = "my_first_layer"))
model.add(layers.Dense(10, activation = "softmax", name = "my_last_layer"))
model.build((None, 3))
model.summary()

 

*Sequential 모델을 사용시

입력과 출력이 하나만 있어야 한다.

 

함수형 API 모델:

 

Sequential 모델과 달리 함수형 API는 리스트와 같은 형태가 아니라 그래프를 닮았다. 또한 Sequential 모델은 하나의 입력과 하나의 출력을 가지며 순서대로 층을 쌓은 모델을 사용해야 하는데 함수형 API는 다중입력과 다중출력을 가진 모델을 사용할 수 있다.

 

- 모델을 만든다

vocabulary_size = 10000
num_tags = 100
num_departments = 4

title = keras.Input(shape = (vocabulary_size, ), name = "title")
text_body = keras.Input(shape = (vocabulary_size,), name = "text_body")
tags = keras.Input(shape = (num_tags, ), name = "tags")

features = layers.Concatenate()([title, text_body, tags])
features = layers.Dense(64, activation = "relu")(features)

prority = layers.Dense(1, activation = "sigmoid", name = "priority")(features)
department = layers.Dense(num_departments, activation = "softmax", name = "department")(features)

model = keras.Model(inputs = [title, text_body, tags],
					outputs = [priority, department])

 

-모델 훈련 및 평가

 

import numpy as np

num_samples = 1200

title_data = np.random.randint(0, 2, size = (num_samples, vocabulary_size))
text_body_data = np.random.randint(0, 2, size = (num_samples, vocabulary_size))
tags_data = np.random.randint(0, 2, size = (num_samples, num_tags))

priority_data = np.random.random(size = (num_samples, 1))
department_data = np.random.randint(0, 2, size = (num_samples, num_departments))

model.compile(optimizer = "rmsprop",
			  loss = ["mean_squared_error", "categorical_crossentropy"],
              metrics = [["mean_absolute_error"], ["accuracy"]]) 
model.fit([title_data, text_body_data, tags_data],
		  [priority_data, department_data],
          epochs = 1)
model.evaluate([title_data, text_body_data, tags_data],
				[priority_data, department_data])

priority_preds, department_preds = model.predict(
	[title_data, text_body_data, tags_data])

 

 

*함수형 API의 장점:

 

함수형은 모델은 그래프 데이터 구조를 하고 있기 때문에 모델 시각화와 특성 추출을 할 수 있다.

 

모델 시각화(위 코드를 시각화한 결과이다)

 

  • Concatenate() 함수를 사용할 시 Input shape을 합치게 된다
  • Output Dense 층은 Dense의 Unit의 수와 같다.

특성추출

 

함수형 API를 사용하면 위 모델에서 중간 특성을 재사용하는 모델을 만들 수 있다. 위 모델을 만든 후 이슈 티켓이 해결되는 걸리는 시간을 구하고 싶어졌다고 하자. 그럼 해당 모델에서 중간층을 뽑아서 새로운 출력층을 출력할 수 있다.

 

features = model.layers[4].output
difficulty = layers.Dense(3, activation = "softmax", name = "difficulty")(features)
new_model = keras.Model(
	inputs = [title, text_body, tags],
    outputs = [priority, department, difficulty])

 

새롭게 만든 모델을 그래프로 출력 해보면..

 

keras.utils.plot_model(
	new_model, "updatad_ticket_classifier.png", show_shapes = True)

이런 특성을 통해 추후에 전이학습 및 특성추출에 사용할 수 있다.

 

Model 서브클래싱

 

마지막으로 알아야 하는 모델 구축 패턴은 Model 서브클래싱이다. 

 

  • __init__() 메서드에서 모델이 사용할 층을 정의한다.
  • call() 메서드에서 앞서 만든 층을 사용하여 모델의 정방향 패스를 정의한다.
  • 서브클래스의 객체를 만들고 데이터와 함께 호출하여 가중치를 만든다.
class CustomerTicketModel(keras.Model):
	def __init__(self, num_departments):
    	super().__init__()
        self.concat_layer = layers.Concatenate()
        self.mixing_layer = layers.Dense(64, activation = "relu")
        self.priority_scorer = layers.Dense(1, activation = "sigmoid")
        self.department_classifier = layers.Dense
        	num_departments, activation = "softmax")
    
    def call(self, inputs):
    	title = inputs["title"]
        text_body = inputs["text_body"]
        tags = inputs["tags"]
        features = self.concat_layer([title, text_body, tags])
        features = self.mixing_layer(features)
        priority = self.priority_scorer(features)
        department = self.department_classifierS(features)
        return priority, department

 

*서브 클래싱의 특징:

 

서브 클래싱은 위 3 모델 중에 가장 유연한 모델이다. 하지만 3 모델 중에서 모델 로직을 많이 책임져야 한다는 단점이 있다. 또한 각 층이 서로 연결되는 방식이 call() 메서드 안에 감추어져 있기 때문에 그래프를 시각화 할 수 없고 특성 추출을 할 수 없다.

 

혼합 모델

 

3가지 종류가 있다고 해서 한가지만 쓸 수 있는 것은 아니다. 3가지 모델들을 사로 상호 운영할 수도 있다.

 

class Classifier(keras.Model):
	def __init__(self, num_classes=2):
    	super().__init__()
        if num_classes == 2:
        	num_units = 1
            activation = "sigmoid"
        else:
        	num_units = num_classes
            activation = "softmax"
        self.dense = layers.Dense(num_units, activation = activation)
    def call(self, inputs):
    	return self.dense(inputs)
        
inputs = keras.Input(shape = (3,))
features = layers.Dense(64, activation = "relu")(inputs)
outputs = Classifier(num_classes = 10)(features)
model = keras.Model(inputs = inputs, outputs = outputs)

이런식으로 서브클래스 모델과 함께 함수형 API를 사용할 수 있다. 큰 틀에서 함수형 API를 사용하면서 워크플로우를 커스터 마이징할 때 서브클래스를 쓰는 것이 바람직하다고 할 수 있다. 

 

워크플로를 커스터마이징 할 수 있는 몇가지 방법이 있다.

 

  • 사용자 정의 측정 지표를 전달한다
  • fit() 메서드에 콜백을 전달하여 훈련하는 동안 특정 시점에 수행될 행동을 예약한다

사용자 정의 측정 지표

 

지표는 모델을 측정하는 도구이다. 기본적으로  keras.metrics에서 제공해주는 모델을  사용할 수 있으나 필요시에 사용자 정의 지표를 만들 수 있어야 한다.

 

이때 사용자 정의 지표를 만든다고 하면, keras.metrics.Metric 클래스를 상속한 클래스를 만들어 주어야 한다. 또한 일반 층과 달라 역전파로 업데이트가 되지 않기 때문에 상태 업데이트 로직을 직접 작성해야 한다.

 

import tensorflow as tf

class RootMeanSquaredError(keras.metrics.Metric): #metric 클래스를 상속 받아야 한다.
	def __init__(self, name = "rmse", **kwargs):
    	super()__init__(name = name, **kwargs)
        self.mse_sum = self.add_weight(name = "mse_sum", initializer = "zeros")
        self.total_samples = self.add_weight(
        	name = "total_samples", initializer = "zeros", dtype = "int32")
            
     def update_state(self, y_true, y_pred, sample_weight = None):
     	y_true = tf.one_hot(y_true, depth = tf.shape(y_pred)[1])
        mse = tf.reduce_sum(tf.square(y_true - y_pred))
        self.mse_sum.assign_add(mse)
        num_samples = tf.shape(y_pred)[0]
        self.total_samples.assign_add(num_samples)

 

콜백 사용하기

 

model.fit()을 사용하여 수십 번의 epoch를 실행하는 것은 종이 비행기를 날리는 것과 비슷하다. 떠난 종이 비행기는 따로 조작할 수 없다. 하지만 콜백을 활용하면 이 종이 비행기를 드론으로 바꾸는 것과 같다.

 

콜백은 모델의 상태와 성능에 대한 모든 정보에 접근하고 훈련 중지, 모델 저장, 가중치 적재 또는 상태 변경 등을 처리할 수 있다.

 

콜백을 사용하느  몇 가지 사례:

 

  • 모델 처크포인트 저장: 훈련하는 동안 어떤 지점에서 모델의 현재 가중치를 저장한다
  • 조기 종료: 검증 손실이 더 이상 향상되지 않을 때 훈련을 중지한다
  • 훈련하는 동안 하이퍼파라미터 값을 동적으로 조정한다: 옵티마이저의 학습률 같은 것
  • 훈련과 검증 지표를 로그에 기록하거나 모델이 학습한 표현이 업데이트될 때마다 시각화한다.