본문 바로가기
Deep Learning/Hands On Machine Learning

17.7 Sparse AutoEncoder

by 대소기 2021. 12. 28.

Sparse AutoEncoder

* Sparse AutoEncoder는 규제 기법을 통해 coding층의 훈련시마다 뉴런 개수를 제한함으로서 각 뉴런들이 더 유용한 특성을 학습하여 coding을 만들어낼 수 있도록 하는 기법이다. 이는 dropout에서 일부 뉴런을 의도적으로 훈련에서 누락시켜 나머지 뉴런들이 더 유용한 특성을 학습하도록 하게 만드는 것과 비슷하다.

* 규제 방법으로는 각 뉴런의 평균 활성화 정도를 측정해서 목표 평균 활성화 정도와 비교해 loss에 더하는 것을 들 수 있다. 예를 들어 각 뉴런이 평균적으로 0.3만큼 활성화 되었고, 목표 평균 활성화 정도가 0.1이라면 loss에 $(0.3 - 0.1)^2$를 더하는 방식을 사용할 수도 있다. 실전에서 더 좋은 방법은 Kullback-leibler divergence를 사용하는 것이다.

 

Kullback- Leibler Divergence

* Kullback-Leibler Divergence(이하 KLD)는 두 확률분포의 차이를 계산할 때 사용되는 함수이다. 여기에서 차이는 entropy의 차이를 뜻한다. 

* Q는 사전 확률분포(가설, 모델 등)이고, P는 사후 확률 분포(실제 관찰 데이터)이다. 그러니까 KLD는 사전 확률에서 사후 확률로 변화하면서 얻은 정보의 양으로 해석할 수 있다.

* KLD를 통해 목표 뉴런의 활성화 정도와 실제 뉴런 활성화 정도의 차이를 계산하면 위와 같다. p는 우리가 사전에 설정해 놓은 뉴런의 목표 활성화 확률이다. q는 실제로 뉴런이 활성화 된, 즉 관찰된 뉴런의 활성화 확률을 뜻한다. 

* 이 KLD를 희소 손실로서 사용한다. KLD는 우리가 설정한 뉴런 활성화 확률에 모델이 얼마나 근접해있는지를 나타내는 지표가 될 것이다. 계산된 손실들을 비용함수의 결과에 더하게 될 것이다. 이 때 희소 가중치를 통해 희소 손실의 중요도를 조정한다. 희소 가중치가 너무 크면 재구성 손실의 영향이 작아져 모델이 적절하게 입력을 재구성하지 못하기 때문에 적절하게 조정할 필요가 있다.

K = keras.backend
kl_divergence = keras.losses.kullback_leibler_divergence

class KLDivergenceRegularizer(keras.regularizers.Regularizer):
    def __init__(self, weight, target=0.1):
        self.weight = weight
        self.target = target
    def __call__(self, inputs):
        mean_activities = K.mean(inputs, axis=0)
        return self.weight * (
            kl_divergence(self.target, mean_activities) +
            kl_divergence(1. - self.target, 1. - mean_activities))
            
tf.random.set_seed(42)
np.random.seed(42)

kld_reg = KLDivergenceRegularizer(weight=0.05, target=0.1)
sparse_kl_encoder = keras.models.Sequential([
    keras.layers.Flatten(input_shape=[28, 28]),
    keras.layers.Dense(100, activation="selu"),
    keras.layers.Dense(300, activation="sigmoid", activity_regularizer=kld_reg) #kld_reg 적용
])
sparse_kl_decoder = keras.models.Sequential([
    keras.layers.Dense(100, activation="selu", input_shape=[300]),
    keras.layers.Dense(28 * 28, activation="sigmoid"),
    keras.layers.Reshape([28, 28])
])
sparse_kl_ae = keras.models.Sequential([sparse_kl_encoder, sparse_kl_decoder])
sparse_kl_ae.compile(loss="binary_crossentropy", optimizer=keras.optimizers.SGD(learning_rate=1.0),
              metrics=[rounded_accuracy])
history = sparse_kl_ae.fit(X_train, X_train, epochs=10,
                           validation_data=(X_valid, X_valid))
                           
# Epoch 1/10
# 1719/1719 [==============================] - 15s 8ms/step - loss: 0.4150 - rounded_accuracy: 0.8121 - val_loss: 0.3716 - val_rounded_accuracy: 0.8564
# Epoch 2/10
# 1719/1719 [==============================] - 19s 11ms/step - loss: 0.3531 - rounded_accuracy: 0.8763 - val_loss: 0.3442 - val_rounded_accuracy: 0.8847
# Epoch 3/10
# 1719/1719 [==============================] - 11s 6ms/step - loss: 0.3340 - rounded_accuracy: 0.8918 - val_loss: 0.3293 - val_rounded_accuracy: 0.8975
# Epoch 4/10
# 1719/1719 [==============================] - 10s 6ms/step - loss: 0.3224 - rounded_accuracy: 0.9018 - val_loss: 0.3213 - val_rounded_accuracy: 0.9043
# Epoch 5/10
# 1719/1719 [==============================] - 10s 6ms/step - loss: 0.3169 - rounded_accuracy: 0.9063 - val_loss: 0.3171 - val_rounded_accuracy: 0.9078
# Epoch 6/10
# 1719/1719 [==============================] - 10s 6ms/step - loss: 0.3135 - rounded_accuracy: 0.9093 - val_loss: 0.3140 - val_rounded_accuracy: 0.9105
# Epoch 7/10
# 1719/1719 [==============================] - 10s 6ms/step - loss: 0.3107 - rounded_accuracy: 0.9117 - val_loss: 0.3115 - val_rounded_accuracy: 0.9130
# Epoch 8/10
# 1719/1719 [==============================] - 10s 6ms/step - loss: 0.3084 - rounded_accuracy: 0.9137 - val_loss: 0.3092 - val_rounded_accuracy: 0.9149
# Epoch 9/10
# 1719/1719 [==============================] - 10s 6ms/step - loss: 0.3063 - rounded_accuracy: 0.9155 - val_loss: 0.3074 - val_rounded_accuracy: 0.9145
# Epoch 10/10
# 1719/1719 [==============================] - 11s 6ms/step - loss: 0.3043 - rounded_accuracy: 0.9172 - val_loss: 0.3054 - val_rounded_accuracy: 0.9169

* 희소 손실을 사용한 결과 전체 뉴런의 평균 활성화 정도가 target인 0.1근처에 위치하게 되었다.

 

ActivityRegularization 층

* keras.layers.ActivityRegularization() 층은 입력을 그대로 반환하면서 training loss에 입력의 절댓값의 합을 더한다. 혹은 층으로 따로 구현하지 않고, 이전 층의 parameter를 activity_regularizer=keras.regularizers.l1(1e-3)으로 설정해도 된다. 이를 통해 coding의 결과 값이 0에 가까워지게 된다. 예제에서는 l1규제를 사용했는데, l2 규제를 사용하면 coding의 모든 값이 0에 가까워지기 때문에 l1규제를 사용한 것이다. l1 규제를 사용하면 모든 값이 제한되는 것이 아니라, 불필요한 것을 제거하고 가장 중요한 코딩을 보전하도록 만든다.

 

'Deep Learning > Hands On Machine Learning' 카테고리의 다른 글

GAN(Generative Adversarial Network)  (0) 2022.01.01
17.8 Variational AutoEncoder  (0) 2021.12.28
17.6 Denoising AutoEncoder  (0) 2021.12.26
17.5 Recurrent AutoEncoder  (0) 2021.12.26
17.4 Convolutional AutoEncoder  (0) 2021.12.26