[CV 5] AutoEncoder
이미지를 효과적으로 압축해줄 수 있는 AutoEncoder에 대해 알아보자. 이것은 데이터 시각화를 위한 차원 축소에도 사용이 가능하다.
🌀 AutoEncoder
Encoder에서 입력 데이터를 저차원 벡터로 압축한 뒤, Decoder에서 원래 크기의 데이터로 복원하는 신경망
Latent Vector
잠재 벡터. 가운데 code표시되어있는 가장 저차원의 벡터
원본 데이터보다 차원이 작으면서도, 원본 데이터의 특징을 잘 보존하고 있는 벡터
AutoEncoder는 궁극적으로 데이터의 중요 특징인, Latent vector를 잘 얻기 위한 방법이다.
Latent vector를 바탕으로 다시 원본 데이터로 복원할 때에 발생하는 오류, 즉 복원 오류(Reconstruction Error)를 최소화하도록 훈련한다. 원본 데이터의 특징을 최대한 보존한다.
Encoder를 잘 만드는 것이 주된 목적이다. (VAE, GAN에서는 decoder를 잘만들어야함.)
AutoEncoder 쓰임새
- 차원 축소, 데이터 압축
- DAE : 데이터 노이즈 제거 (Denoising AutoEncoder)
- Anomaly Detection : 이상치 탐지
- 정상 데이터만 사용해 모델 학습 후, 복원 했을 때의 오차가 임계값을 초과하는 경우, 해당 데이터를 비정상으로 분류
현업시) AutoEncoder에서 정규화가 중요하다고 말씀함.
1. 기본적인 AutoEncoder
원본 데이터, AutoEncoder된 데이터가 어떻게 다른지 보자.
먼저, latent vector 차원 수 정의한다. 다른 신경망과 달리, 이것을 정의 한 뒤에 다른 은닉층 노드 수를 정한다.
Fashion MNIST 이미지 데이터셋 이용
LATENT_DIM = 64 # latent dim은 784보다 작으면 되겠지.
class AutoEncoder(Model):
def __init__(self, latent_dim):
super(AutoEncoder, self).__init__()
self.latent_dim = latent_dim
self.encoder = tf.keras.Sequential([
layers.Flatten(), # 이미지 데이터니까. (28,28)->784
layers.Dense(latent_dim, acitvation='relu')]) # 잠재 벡터 차원수만큼 줄임.
self.decoder = tf.keras.Sequential([
layers.Dense(784, activation='sigmoid'), # 입력노드와 맞춰주려고 다시 784차원으로 늘림
layers.Reshape((28,28))]) # 다시 이미지의 형태로 만들기 위해.
def call(self, x):
encoded = self.encoder(x)
decoded = self.decoder(encoded)
return decoded
model = AutoEncoder(LATENT_DIM)
model.compile(optimizer='adam', loss='mse')
model.fit(x_train, x_train, epochs=10, shuffle=True, validation_data=(x_test,x_test))
# 원본 x_train, autoencoder된 x_train을 넣어줌.
# shuffle=True : 데이터 순서 셔플해줌.
# encoder, decoder이미지를 numpy array로 변환
encoded_imgs = model.encoder(x_test).numpy()
decoded_imgs = model.decoder(enocded_imgs).numpy()
# 원본이미지, autoencoder에서 복원된 이미지를 비교해 보자.
n = 10
plt.figure(figsize=(20,4))
for i in range(n):
# 원본 이미지 출력
ax = plt.subplot(2, n, i+1) # 행의수, 열의수, 인덱스
plt.imshow(x_test[i]) # 28x28
plt.title('Original')
plt.gray()
# x,y열의 axis 보이지 않게 하기
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
# autoencoder에서 나온 이미지
ax = plt.subplot(2, n, i+n+1) # 11~
plt.imshow(decoded_imgs[i])
plt.title('Reconstructed')
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
plt.show() # 이거 해야 성능상 이점있다고하네.
위는 원본 이미지, 밑은 autoencoder사용해 복원된 이미지다.
원본 이미지보단, 해상도가 떨어지지만 , 어느정도 형태는 보존되는 것을 볼 수 있다.
2. DAE (Denoising AutoEncoder)
이번 코딩은 위와 다르게 Dense로 구성된 것이 아니라, Convolution 층이 사용된 autoencoder를 해보자 (Convolutional AutoEncoder) → 이것을 하기 위해선, 데이터에 임의의 채널을 더하는 과정 필요
random noise
원본 이미지에 노이즈 추가해줌. noisy를 어떻게 주느냐가 중요하다.
noise데이터가 뭐냐에 따라 다른 형태로 해줘야함.(비 noisef,,a..)
# 정규분포에서 20%만큼의 노이즈를 추가
noise_factor=0.2
x_train_noisy = x_train + noise_factor*tf.random.normal(shape=x_train.shape)
x_test_noisy = x_test + noise_factor*tf.random.normal(shape=x_test.shape)
# 텐서 값을 지정된 0~1 최소최대로 자른다.
x_train_noisy = tf.clip_by_value(x_train_noisy, clip_value_min=0, clip_value_max=1)
x_test_noisy = tf.clip_by_value(x_test_noisy, clip_value_min=0, clip_value_max=1)
class Denoise(Model):
def __init__(self):
super(Denoise, self).__init__()
self.encoder = tf.keras.Sequential([
layers.Input(shape=(28,28,1)),
# Downsampling
layers.Conv2D(16,(3,3), acivation='relu', padding='same', strides=2),
layers.Conv2D(8,(3,3), activation='relu', padding='same', strides=2)])
self.decoder = tf.keras.Sequential([
# Upsampling
layers.Conv2DTranspose(8, kernel_size=3, strides=2, activation='relu', padding='same'),
layers.Conv2DTranspose(16, kernel_size=3, strides=2, activation='relu', padding='same'),
layers.Conv2D(1, kernel_size=(3,3), activation='sigmoid', padding='same')])
# input_shape=(28,28,1)이니까 채널 1을 맞춰주기위해 1로함.
def call(self, x):
encoded = self.encoder(x)
decoded = self.decoder(encoded)
return decoded
model = Denoise()
model.compile(optimizer='adam', loss='mse')
model.fit(x_train_noisy, x_train, epochs=10, shuffle=True, validation_data=(x_test_noisy, x_test))
# ouput은 noise가 없는 것으로 나오게끔 훈련.
위는 x_test_noisy이고, 밑은 decoded_img(x_test)한것.
입력 데이터는 Noise가 있는 데이터, target 데이터는 원본 이미지로.
결과 ) noise가 없는 데이터로 잘 복원됨.
Anomaly Detection (이상치 탐지)
비정상적인 데이터를 탐지하기 위해 AutoEncoder 사용
AutoEncoder는 잠재 벡터를 바탕으로, 다시 원본 데이터로 복원할 때 발생하는 오류, 즉 복원 오류를 최소화하도록 훈련된다.
정상 데이터로만 훈련한 뒤, 비정상 데이터셋을 복원한다면 복원 오류가 커질 것이다. 복원 오류가 특정한 임계값 초과 시, 비정상으로 판단
physoiNet : 의학데이터 자유롭게 이용할 수 있다.
심전도 측정 데이터셋인 ECG5000 데이터셋
label : 0: 비정상 / 1:정상
# 데이터 받아오기
# MinMaxScaling
min_val = tf.reduct_min(train_data) # 텐서 차원 전체에서 요소의 최소값 계산
max_val = tf.reduct_max(train_data)
train_data = (train_data-min_val) / (max_val-min_val)
test_data = (test_data-min_val) / (max_val-min_val)
# 타입 변환
train_data = tf.cast(train_data, tf.float32)
test_data = tf.cast(test_data, tf.float32)
# label
# 인덱스로 사용하기 위해 bool형태로 변환
train_label = train_label.astype(bool)
test_label = test_label.astype(bool)
# true인 값만 가져오기
normal_train_data = train_data[train_label]
normal_test_data = teset_data[test_label]
# false인 값만 가져오기
anomalous_train_data = train_data[~train_label]
anomalous_test_data = test_data[~test_label]
class AnomalyDetector(Model):
def __init__(self):
super(AnomalyDetector, self).__init__()
self.encoder = tf.keras.Sequential([
layers.Dense(32, activation='relu'),
layers.Dense(16, activation='relu'),
layers.Dense(8, activation='relu')])
self.dcoder = tf.keras.Sequential([
layers.Dense(16, activation='relu'),
layers.Dense(32, actiavtion='relu'),
layers.Dense(140, activation='sigmoid')])
# 마지막 층은 feature수 140개로 맞춰줌.
def call(self, x):
encoded = self.encoder(x)
decoded = self.decoder(encoded)
return decoded
model = AnomalyDetector()
model.compile(optimizer='adam', loss='mae')
history = model.fit(normal_train_data, normal_train_data,
epochs=20, batch_size=512,
validation_data=(test_data, test_data), shuflle=True)
복원 오류 시각화
정상데이터, 복원 오류 결과 시각화해보자.
encoded_imgs = model.encoder(normal_test_data).numpy()
decoded_imgs = model.decoder(encoded_imgs).numpy()
plt.plot(normal_test_data[0], 'b')
plt.plot(decoded_imgs[0], 'r')
plt.fill_between(np.arange(140), decoded_imgs[0], normal_test_data[0], color='lightcoral')
plt.legend(labels=['Input', 'Reconstruction', 'Error'])
plt.show()
차이가 면적으로 보이지.
비정상데이터와 복원 오류 시각화
훈련 오차를 시각화해보고 임계값 정하기
reconstructions = model.predict(normal_train_data)
train_loss = tf.keras.losses.mae(reconstructions, normal_train_data)
# 임계값 지정
threshold = np.mean(train_loss)+np.std(train_loss)
threshold
>> 0.03258
고정된 임계값보다 클 경우 이상치로 판단
def predict(model, data, threshold):
reconstructions = model(data)
loss = tf.keras.losses.mae(reconstructions, data)
return tf.math.less(loss, threshold) # 각각의 위치값보고 <면 반환
임계값 넘어가는 애들은 제거하고 예측
지금까지 AutoEncoder에 대해 알아보았다. AE의 가장 큰 목적은 차원 축소이다. 그런데 왜 차원 축소를 해야할까? 이 이유는 Manifold learning을 이해하면 알 수 있다.
Manifold Learning
차원축소는 Manifold Learning과 같은 의미이다. 따라서 AE의 궁극적인 목적은 Manifold라고도 할 수 있다.
AutoEncoder는 데이터가 퍼져있는 공간에서 데이터의 Manifold를 찾아내어 학습하는 과정이라고 할 수도 있다.
Manifold Learning : 고차원의 공간에서 데이터가 이루어지는 저차원의 공간이 매니폴드이며, 매니폴드 공간을 찾는 것이다.
고차원 데이터 공간에 samples들을 잘 아우르는 subspace가 있을 것이라는 가정 하에, 학습을 진행하여 manifold를 통해 데이터의 차원을 축소하는 것이다.
Manifold 학습 절차
- 데이터를 고차원 공간에 뿌린다.
- 해당 데이터를 오류 없이 잘 아우르는 subspace(=manifold)를 구한다.
- 데이터를 manifold에 projection하면 데이터 차원 축소된다.
Manifold 목적
- 데이터 압축 : 데이터 압축후, latent vector로 디코딩 시 원본 데이터 복원이 잘 된다면 압축이 잘 된것이지!
- 데이터 시각화
- 차원의 저주 피하기 : 데이터 차원이 늘어날수록 공간의 부피는 기하급수적으로 증가하여 밀도는 희박해진다. 밀도가 희박해지면 데이터 활용력이 떨어지므로 가능한 차원은 줄일수록 좋다.
- 유용한 특징 추출 : manifold를 구하면 중요한 feature가 추출된다. 축을 따라가면 size, rotation 변화 등 특징을 발견할 수 있다.
- 고차원의 데이터를 잘 표현한다 = 데이터의 중요한 feature 잘 찾는다. 중요한 특징들을 찾았다면 이 특징을 공유하는 sample들도 잘 찾을 수 있어, 의미적으로 가까운 데이터들을 찾을 수 있다.
- 고차원의 데이터를 Euclidean 거리상에서 중앙값을 구한다면 비정상적인 값이 나온다. manifold를 벗어낫기 때문이다. 따라서 정상적인 중앙값을 구하려면 manifold안에서 구해야한다.
PCA와 AutoEncoder의 차이점
PCA는 linear 차원축소방법이고, AE는 nonlinear 차원축소방법이다.
아무래도 데이터를 더 효과적으로 아우르는 subspace를 찾을 수 있으니, linear 차원 축소보다 nonlinear차원축소가 더 효과적일 것이다.
또다른 AutoEncoder에 대해 알아보자.
VAE (Varaiational AutoEncoder)
VAE와 AE는 다르다!!!!!!!!!!!!!!!!!
- AE : Manifold Learning
- VAE : Generative Model
수학적으로 AutoEncoder와는 별로 관련이 없으며, 목적도 다르지만, 네트워크 구조가 비슷하기 때문에 VAE라는 이름이 붙었다.
AE의 목적은 Manifold learning으로 Encoder부분이 중요하다고 배웠다. Decoder부분은 압축이 잘 됐는지 검증을 위해 붙은 것뿐이다. 이 부분에서 VAE는 정반대이다. 데이터를 만들어내는 것이 목적인 Generative model이다. (다음에 등장할 GAN과 연결되는 내용) 그러므로, VAE는 뒷단이 중요하다! 뒷단을 학습시켜버렸더니 앞단이 붙었는데, 공교롭게 구조가 AE와 같아진 것 뿐이다.!!
VAE의 목적
고양이 샘플이 있다면, 학습을 통해 기존의 고양이와 유사한 새로운 고양이 샘플을 생성해내는 것이다.
https://deepinsight.tistory.com/127
더 공부