-
[HandsOn]12. 텐서플로를 사용한 사용자 정의 모델과 훈련 - 내용 정리 2[도서완독]Hands On Machine Learning 2022. 7. 20. 18:40
뭔가 어려워보인다... 힘내서 시작하자!!
아자아자😍
12.3 사용자 정의 모델과 훈련 알고리즘
12.3.1 사용자 정의 손실 함수
def huber_fn(y_true, y_pred): error = y_true - y_pred is_small_error = tf.abs(error) < 1 squared_loss = tf.square(error) / 2 linear_loss = tf.abs(error) - 0.5 return tf.where(is_small_error, squared_loss, linear_loss)
이런 식으로 내가 만들고 싶은 손실 함수를 만들 수 있다.
model.compile(loss=huber_fn, optimizer='nadam') model.fit(X_train, y_train, [...])
그리고 이렇게 컴파일해서 훈련 가능!
12.3.2 사용자 정의 요소를 가진 모델을 저장하고 로드하기
모델을 로드할 때는 함수 이름과 실제 함수를 매핑한 딕셔너리를 전달해야 한다.
model = load_model("my_model_with_a_custom_loss.h5", custom_objects={"huber_fn": huber_fn})
만약 내가 만든 함수에 매개변수를 지정하고 싶다면?
def create_huber(threshold=1.0): def huber_fn(y_true, y_pred): error = y_true - y_pred is_small_error = tf.abs(error) < threshold squared_loss = tf.square(error) / 2 linear_loss = threshold * tf.abs(error) - threshold**2 / 2 return tf.where(is_small_error, squared_loss, linear_loss) return huber_fn model.compile(loss=create_huber(2.0), optimizer="nadam")
근데 이렇게 만들면... 모델을 로드할 때도 변수값을 지정해 줘야 한다.
model = load_model("my_model_with_a_custom_loss_threshold_2.h5", custom_objects={"huber_fn": create_huber(2.0)})
이걸 해결하려면?
from tensorflow.keras.losses import Loss class HuberLoss(Loss): def __init__(self, threshold=1.0, **kwargs): self.threshold = threshold super().__init__(**kwargs) def call(self, y_true, y_pred): error = y_true - y_pred is_small_error = tf.abs(error) < self.threshold squared_loss = tf.square(error) / 2 linear_loss = self.threshold * tf.abs(error) - self.threshold**2 / 2 return tf.where(is_small_error, squared_loss, linear_loss) def get_config(self): base_config = super().get_config() return {**base_config, "threshold": self.threshold}
get_config 메서드를 구현하면 된다.
model.compile(loss=HuberLoss(2.), optimizer="nadam")
모델 컴파일시 해당 클래스를 사용할 수 있고, 모델을 저장할 때 임곗값도 함께 저장된다. 임곗값을 지정해 줄 필요 없음!
모델 로드할 때 이것도 이름이랑 클래스를 매핑해주어야 한다.
model = load_model("my_model_with_a_custom_loss_class.h5", custom_objects={"HuberLoss": HuberLoss})
12.3.4 사용자 정의 지표
이건 왠지 내가 써먹을 것 같아서 자세히 공부하기로...
from tensorflow.keras.metrics import Metric import tensorflow as tf class HuberMetric(Metric): def __init__(self, threshold=1.0, **kwargs): super().__init__(**kwargs) self.threshold = threshold self.huber_fn = create_huber(threshold) self.total = self.add_weight('total', initializer='zeros') self.count = self.add_Weight('count', initializer='zeros') def update_state(self, y_true, y_pred, sample_weight=None): metric = self.huber_fn(y_true, y_pred) self.total.assign_add(tf.reduce_sum(metric)) self.count.assign_add(tf.cast(Tf.size(y_true), tf.float32)) def result(self): return self.total / self.count def get_config(self): base_config = super().get_config() return {**base_config, "threshold":self.threshold}
사용자 정의 지표도 Metric을 상속받기 때문에 대충 정해진 형식이 있는듯.. document를 참조.
Metric을 상속받아 사용자 정의 지표를 만든다. 이 때 생성자에서 add_weight() 메서드를 통해 값이 하나인 변수 total, count를 정의한다.
update_state 메서드에서, 내가 만들어놨던 huber_fn 함수로 metric 객체를 만들고, sum해서 total에 할당한다.
마찬가지로 count 변수에도 y_true의 개수를 누적해서 쌓는다.
result 메서드에서 전체 손실을 count 수로 나눈다.
12.3.5 사용자 정의 층
from tensorflow.keras.layers import Layer class MyDense(Layer): def __init__(self, units, activation=None, **kwargs): super().__init__(**kwargs) self.units = units self.activation = tf.keras.activations.get(activation) def build(self, batch_input_shape): self.kernel = self.add_weight( name='kernel', shape=[batch_input_shape[-1], self.units], initializer = 'glorot_normal' ) self.bias = self.add_weight( name='bias', shape=[self.units], initializer='zeros' ) super().build(batch_input_shape) def call(self, X): return self.activation(X @ self.kernel + self.bias) def comput_output_shape(self, batch_input_shape): return tf.TensorShape(batch_input_shape.as_list()[:-1] + [self.units]) def get_config(self): base_config = super().get_config() return {**base_config, 'units':self.units, 'activation': tf.keras.activations.serialize(self.activation)}
자세한 건 document 참고.
class MyMultiLayer(tf.keras.layers.Layer): def call(self, X): X1, X2 = X return [X1 + X2, X1 * X2, X1 / X2] def compute_output_shape(self, batch_input_shape): b1, b2 = batch_input_shape return [b1, b1, b1]
이 코드가 이해가 안됨....
compute_output_shape() 메서드가 이해가 안돼 주륵..,
keras 문서 보니까 call 이든 뭐든 메서드는 이미 다 정해져 있고 오버라이딩만 하면 되는거 같은데..,
이게 왜 인풋 2개 아웃풋 3개란말임? 걍 call() 메서드만 보면 그런데....
compute_output_shape는 뭐지그럼?ㅋㅋㅋㅋ 일단 패스....
훈련과 테스트에서 다르게 동작하는 층이 필요하다면 call() 메서드에 training 매개변수를 추가하면 됨.
class MyGaussianNoise(tf.keras.layers.Layer): def __init__(self, stddev, **kwargs): super().__init__(**kwargs) self.stdd ev = stddev def call(self, X, training=None): if training: noise = tf.random.normal(tf.shape(X), stddev=self.stddev) return X + noise else: return X def compute_output_shape(self, batch_input_shape): return batch_input_shape
이건 훈련하는 동안 규제 목적으로 가우스 잡음을 추가하고, 테스트할 때는 규제를 하지 않는 레이어임!
하 class를 계속 보다 보니 토할것같다 다음은 사용자 정의 모델!
힘내서 계속 가보자 😂
'[도서완독]Hands On Machine Learning' 카테고리의 다른 글
[HandsOn]13. 텐서플로에서 데이터 적재와 전처리하기 - 내용 정리1 (0) 2022.07.26 [HandsOn]12. 텐서플로를 사용한 사용자 정의 모델과 훈련 - 연습문제 풀이 (0) 2022.07.21 [HandsOn]12. 텐서플로를 사용한 사용자 정의 모델과 훈련 - 내용 정리 1 (0) 2022.07.19 [HandsOn]11. 심층 신경망 훈련하기 - 내용 정리 3 (0) 2022.07.08 [HandsOn]11. 심층 신경망 훈련하기 - 내용 정리 2 (0) 2022.07.06