Open givitallugot opened 1 year ago
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
# tf.where(bool type 텐서, True일 때 출력값, False일 때 출력값)
return tf.where(is_small_error, squared_loss, linear_loss)
model.compile(loss=huber_fn, optimizer="nadam") model.fit(X_train, y_train, [...])
- (주의) 성능을 위해서는 위처럼 벡터화하여 구현해야하고, 텐서플로 그래프의 장점을 활용하려면 텐서플로 연산만 사용해야 함
### 후버 손실 Huber loss
<img width="344" alt="image" src="https://user-images.githubusercontent.com/26505830/219964633-e5531e4e-b1b3-4047-92d8-dc33a9d71ed3.png">
- MSE와 MAE를 절충
- 일정 범위 δ (델타)를 정해서 그 안에 있으면 오차(잔차. 보통 관측값과 예측값의 차이) 를 제곱하고, 그 밖에 있으면 오차의 절대값을 구하는 것
- 1/2가 붙는 이유는 δ에서 오차 제곱과 오차 절대값이 만날 때 매끄럽게 이어지도록 하게 만들기 위함
- <img width="458" alt="image" src="https://user-images.githubusercontent.com/26505830/219964880-e426064c-80e4-415c-a238-6da20594ede8.png">
- `a = y - f(x)` 로 확장될 수 있음.
- 녹색: δ=1. 후버 손실
- 파랑: y-f(x). 제곱 오차 손실
- 참고
- https://blog.naver.com/PostView.naver?blogId=jws2218&logNo=221890882708&parentCategoryNo=&categoryNo=12&viewDate=&isShowPopularPosts=true&from=search
- https://soki.tistory.com/m/16
사용자 정의 손실 함수 사용하는 모델 저장
사용자 정의 손실 함수 사용하는 모델 로드
model = keras.models.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")
모델 저장시 threshold
값이 저장되지 않으므로 모델 로드시 threshold
값을 지정해야 함
model = keras.models.load_model("my_model_with_a_custom_loss_threshold_2.h5",
custom_objects={"huber_fn": create_huber(2.0)")
keras.losses.Loss
클래스 상속하고 get_config()
메서드를 구현하면 threshold
값도 저장 가능
class HuberLoss(keras.losses.Loss):
def __init__(self, threshold=1.0, **kwargs):
self.threshold = threshold
# 기본적인 하이퍼파라미터를 받은 매개변수 값을 전달 (손실함수의 name, 개별 샘플의 손실을 모으기 위해 사용할 reduction 알고리즘)
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 = threshold * tf.abs(error) - threshold**2 / 2
return tf.where(is_small_error, squared_loss, linear_loss)
# 하이퍼파라미터 이름과 같이 매핑된 딕셔너리를 반환
def get_config(self):
base_config = super().get_config()
# threshold 하이퍼파라미터 추가
return {**base_config, "threshold": self.threshold}
model.compile(loss=HuberLoss(2.), optimizer="nadam")
model = keras.models.load_model("my_model_with_a_custom_loss_class.h5", custom_objects={"HuberLoss": HuberLoss})
- 모델 저장시 케라스는 손실 객체의 `get_config()` 메서드를 호출하여 반환되 설정을 HDF5 파일에 JSON 형태로 저장
- 모델 로드시 `HuberLoss` 클래스의 `from_config()` 클래스 메서드를 호출
- 이 메서드는 생성자에게 `**config` 매개변수를 전달해 클래스 인스턴스를 만듦
# 사용자 정의 활성화 함수
def my_softplus(z):
return tf.math.log(tf.exp(z) + 1.0)
def my_glorot_initializer(shape, dtype=tf.float32): stddev = tf.sqrt(2. / (shape[0] + shape[1])) return tf.random.normal(shape, stddev = stddev, dtype=dtype)
def my_l1_regularizer(weights): return tf.reduce_sum(tf.abs(0.01 * weights))
def my_positive_weights(weights): return tf.where(weights < 0., tf.zeros_like(weights), weights)
layer = keras.layers.Dense(30, activation = my_softplus, kernel_initializer = my_glorot_initializer, kernel_regularizer=my_l1_regularizer, kernel_constraint=my_positive_weights)
- 함수가 모델과 함께 저장해야 할 하이퍼파라미터를 가지고 있다면 keras.regularizers.Regularizer, keras.constraints.Constraint, keras.initializers.Initializer, keras.layers.Layer와 같이 적절한 클래스 상속
```python
# l1 규제를 위한 간단한 클래스
class MyL1Regularizer(keras.regularizers.Regularizer):
def __init__(self, factor):
self.factor = factor
def __call__(self, weights): # call() 메서드를 구현해야 함
return tf.reduce_sum(tf.abs(self.factor * weights))
def get_config(self):
return {"factor": self.factor}
1: 12.3.1 2: 12.3.2 3: 12.3.3
02/22 (수) 10:10