17. 오토인코더와 GAN
오토인코더
지도 없이도 잠재 표현 또는 코딩이라 부르는 입력 데이터의 밀집 표현을 학습할 수 있는 신경망
- 코딩은 일반적으로 입력보다 훨씬 낮은 차원을 가지므로 오토인코더가 차원 축소, 특히 시각화에 사용됨
(오토인코더가 강력한 특성 추출기처럼 작동하므로 심층 신경망의 비지도 사전훈련에 사용되는 것)
- 일부 오토인코더는 훈련 데이터와 매우 비슷한 새로운 데이터를 생성할 수 있음 -> 생성 모델
생성적 적대 신경망 (GAN)
초해상도(이미지 해상도 높이기), 이미지를 컬러고 바꾸기, 강력한 이미지 편집, 간단한 스케치->실제 이미지, 동영상에서 다음 프레임 예측하기, 데이터 증식, 다른 모델의 취약점 식별 및 개선 등 널리 사용됨
오토인코더와 GAN 모두 비지도 학습으로, 둘 다 밀집 표현을 학습하고 생성 모델로 사용할 수 있음
오토인코더
- 단순히 입력을 출력으로 복사하는 방법을 배움
- 네트워크에 제약을 가해, 이 작업을 어렵게 만듦 (잠재 표현의 크기를 제한하거나, 입력에 잡음을 추가하고 원본 입력을 복원하도록 네트워크를 훈련)
- 오토인코더가 바로 복사하지 못하고, 데이터를 효율적으로 표현하는 방법을 배움
- 즉, 코딩은 일정 제약 조건하에, 항등 함수를 학습하려는 오토인코더의 노력으로 생긴 부산물
GAN
- 생성자와 판별자로 구성
- 생성자는 훈련 데이터와 비슷하게 보이는 데이터를 생성
- 판별자는 가짜 데이터와 진짜 데이터를 구별함
- 신경망이 훈련하는 동안 생성자와 판별자가 서로 경쟁함 -> 적대적 훈련
17. 1 효율적인 데이터 표현
- 위 두 숫자 시퀀스를 외울 때, 처음 봤을 땐 첫 번째 시퀀스가 짧으므로 더 쉬워보이지만,
- 두 번째 시퀀스를 잘 보면 50부터 14까지 짝수를 나열했음을 알 수 있음
-> 이런 패턴을 알면 두 번째 시퀀스를 외우는 것이 더 쉬움
-> 즉, 긴 시퀀스를 기억할 때 패턴을 찾는 것이 유용함
=> 오토인코더의 경우, 훈련하는 동안 오토인코더에 제약을 가해서 데이터에 있는 패턴을 찾아 활용함
체스 플레이어가 체스 패턴을 통해 체스판의 말 위치를 외우는 것 처럼,
오토인코더는 입력을 받아 효율적인 내부 표현으로 바꾸고 입력과 가장 가까운 어떤 것을 출력함
오토인코더는 항상 두 부분으로 구성
- 입력을 내부 표현으로 바꾸는 인코더 (인지 네트워크)
- 내부 표현을 출력으로 바꾸는 디코더 (생성 네트워크)
출력층의 뉴런 수가 입력 개수와 동일하다는 것을 제외하면, 오토인코더는 다층 퍼셉트론과 구조가 동일
- 뉴런 두 개로 구성된 하나의 은닉층 (인코더)
- 뉴런 세 개로 구성된 출력층 (디코더)
- 오토인코더가 입력을 재구성하기 때문에 출력을 재구성 이라고 부르고,
- 비용 함수는 재구성이 입력과 다를 때 모델에 벌점을 부과하는 재구성 손실을 포함함
내부의 표현이 입력 데이터보다 저차원(3차원 -> 2차원)이기 때문에, 이런 오토인코더를 과소완전(undercomplete) 이라고 함
- 과소완전 오토인코더는 입력을 코딩으로 간단히 복사할 수 없으며, 입력과 똑같은 것을 출력하기 위한 방법을 찾아야함
-> 이를 통해 입력 데이터에서 가장 중요한 특성을 학습하도록 만듦
- 적어도 입력 데이터에 대해서는 복원(재구성)을 잘한다는 특징이 있음
17. 2 과소완전 선형 오토인코더로 PCA 수행하기
PCA
- 차원 축소 방법 중 하나로, 선형적으로 데이터 차원을 감소시킴
-데이터가 퍼져있는 방향으로 좌표축을 이동하는 방식으로,
원본 데이터의 분산을 최대로 보존하는 저차원의 초평면을 찾고자함
오토인코더가 선형 활성화 함수 + 비용함수 MSE 라면, PCA를 수행하는 것과 같음
3D 데이터 셋에 PCA를 적용해 2D에 투영하는 선형 오토인코더
from tensorflow import keras
encoder = keras.models.Sequential([keras.layers.Dense(2, input_shape=[3])])
decoder = keras.models.Sequential([keras.layers.Dense(3, input_shape=[2])])
autoencoder = keras.models.Sequential([encoder, decoder])
autoencoder.compile(loss="mse", optimizer=keras.optimizers.SGD(lr=0.1))
- 오토인코더는 인코더 다음에 디코더가 뒤따르는 Sequential 모델
- 오토인코더의 출력 개수 = 입력 개수 (여기서는 3개)
- 단순한 PCA 수행을 위해서는 활성화 함수 사용 X (= 모든 뉴런이 선형), 비용함수는 MSE
3D 데이터셋에 훈련 후 모델을 사용해 동일한 데이터셋을 인코딩 (= 2D로 투영)
history = autoencoder.fit(X_train, X_train, epochs=20)
codings = encoder.predict(X_train)
- 동일한 데이터셋 X_train 이 입력과 타깃에도 사용됨
원본 3D 데이터셋(왼쪽)과 은닉층 출력(코딩 층, 오른쪽)
- 오토인코더는 PCA처럼 데이터에 있는 분산이 가능한 많이 보존되도록 데이터를 투영할 최상의 2D 평면을 찾음
17. 3 적층 오토인코더, Stacked AutoEncoder
은닉층을 여러 개 가지는 오토인코더
- 층을 더 추가하여 더 복잡한 코딩을 학습할 수 있음
- 오토인코더가 너무 강력해지지 않도록 주의
- 인코더가 너무 강력해서 각각의 입력 데이터를 임의의 한 숫자로 매핑하도록 학습했다고 가정
- 훈련 데이터를 완벽히 재구성하겠지만, 유용한 데이터 표현을 학습하지 못할 것
- 적층 오토인코더는 전형적으로 가운데 은닉층(코딩 층)을 기준으로 대칭
- 위 예시는 입력 784개, 뉴런 300개로 된 은닉층, 뉴런 150개로 된 가운데 은닉층, 뉴런 300개로 된 은닉층, 뉴런 784개로 된 출력층
17. 3. 1 케라스를 사용하여 적층 오토인코더 구현하기
패션 MNIST 데이터셋에서 SELU 활성화 함수를 사용한 적층 오토인코더
stacked_encoder = keras.models.Sequential([
keras.layers.Flatten(input_shape=[28, 28]),
keras.layers.Dense(100, activation="selu"),
keras.layers.Dense(30, activation="selu"),
])
stacked_decoder = keras.models.Sequential([
keras.layers.Dense(100, activation="selu", input_shape=[30]),
keras.layers.Dense(28 * 28, activation="sigmoid"),
keras.layers.Reshape([28, 28])
])
stacked_ae = keras.models.Sequential([stacked_encoder, stacked_decoder])
stacked_ae.compile(loss="binary_crossentropy",
optimizer=keras.optimizers.SGD(lr=1.5))
history = stacked_ae.fit(X_train, X_train, epochs=10,
validation_data=(X_valid, X_valid))
- 오토인코더 모델을 인코더와 디코더 두 모델로 나눔
- 인코더는 28x28 픽셀의 흑백 이미지를 받고, 이미지를 784 크기의 벡터로 표현하기 위해 펼침
- SELU 활성화 함수를 사용하는 Dense 층 두 개에 통과시킴
- 각 입력 이미지에 대해 인코더는 크기가 30인 벡터를 출력함
- 디코더는 크기가 30인 코딩을 받아, 크기가 커지는 Dense 층 두 개에 통과시킴
- 최종 벡터를 28x28 배열로 변경하여 디코더의 출력이 인코더의 입력과 동일한 크기가 되도록 함
- 적층 인코더를 컴파일할 때 평균 제곱 오차 대신 이진 크로스 엔트로피 손실을 사용
- 재구성 작업을 다중 레이블 이진 분류 문제로 다루는 것
- 각 필셀의 강도는 픽셀이 검정일 확률을 나타냄
- X_train을 입력과 타깃으로 사용해 모델을 훈련함 (X_valid를 검증 입력과 검증 타킷으로 사용)
17. 3. 2 재구성 시각화
오토인코더가 잘 훈련되었는지 확인하는 방법은 입력과 출력을 비교하는 것
- 입력과 출력의 차이가 크지 않아야 함
def plot_image(image):
plt.imshow(image, cmap="binary")
plt.axis("off")
def show_reconstructions(model, n_images=5):
reconstructions = model.predict(images[:n_images])
fig = plt.figure(figsize=(n_images * 1.5, 3))
for image_index in range(n_images):
plt.subplot(2, n_images, 1 + image_index)
plot_image(images[image_index])
plt.subplot(2, n_images, 1 + n_images + image_index)
plot_image(reconstructions[image_index])
show_reconstructions(stacked_ae)
- 재구성된 이미지를 식별할 수는 있지만, 정보를 많이 읽은 모습
- 모델을 더 훈련하고 인코더/디코더 층을 늘리거나 코딩을 늘리면 나아질 수 있으나, 네트워크가 너무 강력하면 데이터에서 유익한 패턴을 학습하지 못하고 완벽한 재구성 이미지를 만드려고할 것
17. 3. 3 패션 MNIST 데이터셋 시각화
훈련한 적층 오토인코더 모델을 사용해, 데이터 셋의 차원을 축소할 수 있음
- 시각화 입장에서 보면, 다른 차원 축소 알고리즘만큼의 성능은 아니지만
- 샘플과 특성이 많은 대용량 데이터셋을 다룰 수 있음
- 오토인코더를 사용해 적절히 차원을 축소하고, 다른 차원 축소 알고리즘을 사용해 시각화하는 것도 방법
왜 대용량 고차원 데이터셋을 차원 축소하는 데 적합한가?
8장 차원축소 장에서 잠깐 다루었던 LLE, Isomap 등은 각 샘플 별로 근방에 있는 데이터를 사용하는 neighborhood 기반 방법임
-> 즉, 가까운 애들이 manifold 상에서도 가까울 것이라고 가정하는 것
-> 하지만, 의미적으로 가까울 것이라고 생각하는 샘플들이 가깝지 않을 수 있음
(고차원 데이터 간의 유클리디안 거리 != 유의미한 거리)
-> 데이터 공간이 고차원일 수록, 매니폴드를 찾기 어려울 수록
-> neighborhood방식이 아닌 DNN 기반의 오토인코더가 유용할 수 있음
패션 MNIST 데이터셋 시각화
- 적층 오토인코더의 인코더 모델을 사용해 차원을 30으로 줄이고,
- t-SNE 알고리즘을 구현한 사이킷런 클래스를 통해 차원을 2까지 줄임
t-SNE
비슷한 샘플은 가까이, 비슷하지 않은 샘플은 멀리 떨어지도록 하면서 차원을 축소
(거리가 가까운 샘플의 정보를 우선 보존하기 위함)
- 주로 시각화에 많이 사용
- 특히 고차원 공간에 있는 샘플의 군집을 시각화할 때 사용
(ex. MNIST 데이터셋을 2D로 시각화할 때)
from sklearn.manifold import TSNE
X_valid_compressed = stacked_encoder.predict(X_valid)
tsne = TSNE()
X_valid_2D = tsne.fit_transform(X_valid_compressed)
데이터 셋을 그래프로 표현
plt.scatter(X_valid_2D[:, 0], X_valid_2D[:, 1], c=y_valid, s=10, cmap="tab10")
- 각 클래스를 다른 색으로 표현
- t-SNE 알고리즘이 식별한 클러스터가 클래스와 잘 매칭이 됨
17. 3. 4 적층 오토인코더를 사용한 비지도 사전훈련
11장에서 다루었던 것 처럼,
비슷한 문제를 학습한 신경망을 찾아 하위층을 재사용하는 방법을 통해, 적은 훈련 데이터를 사용해 고성능 모델을 훈련할 수 있음
레이블된 훈련 데이터가 많지 않을때 적층 오토인코더를 통해 훈련하기
- 대부분 레이블되지 않은 대량의 데이터셋을, 먼저 전체 데이터셋을 사용해 적층 오토인코더를 훈련
- 그 다음, 오토인코더의 하위층을 재사용해 실제 문제를 해결하기 위한 신경망을 만들고, 레이블된 데이터를 사용해 훈련
구현도 아래와 같이 가능
1. 레이블된 것, 레이블되지 않은 모든 훈련 데이터를 사용해 오토인코더를 훈련
2. 그 후 인코더 층을 재사용하여 새로운 신경망을 만듦
3. 레이블된 데이터를 통해 훈련
17. 3. 5 가중치 묶기
위 예제처럼, 오토인코더가 대칭일 땐, 디코더의 가중치와 인코더의 가중치를 묶는 것이 일반적
- 모델에 있는 가중치의 수를 절반으로 줄여 훈련 속도를 높이고, 과대적합의 위험을 줄임
어떤 오토인코더가 N개 층을 갖고, WL이 L번째 층의 가중치를 나타낼 때
- (1은 첫 번째 은닉층, N/2은 코딩 층, N은 출력층)
- 디코더 층의 가중치는 WN-L+1 = WL^T (L = 1,2, ..., N/2)
케라스의 사용자 정의 층을 만들어 층 간 가중치 묶기
class DenseTranspose(keras.layers.Layer):
def __init__(self, dense, activation=None, **kwargs):
self.dense = dense
self.activation = keras.activations.get(activation)
super().__init__(**kwargs)
def build(self, batch_input_shape):
self.biases = self.add_weight(name="bias",
shape=[self.dense.input_shape[-1]],
initializer="zeros")
super().build(batch_input_shape)
def call(self, inputs):
z = tf.matmul(inputs, self.dense.weights[0], transpose_b=True)
return self.activation(z + self.biases)
- 일반적인 Dense 층과 비슷하지만, 다른 Dense 층의 전치된 가중치를 사용함
- 편향 벡터는 독자적으로 사용
위 사용자 정의층을 사용하여 적층 오토인코더 구성
dense_1 = keras.layers.Dense(100, activation="selu")
dense_2 = keras.layers.Dense(30, activation="selu")
tied_encoder = keras.models.Sequential([
keras.layers.Flatten(input_shape=[28, 28]),
dense_1,
dense_2
])
tied_decoder = keras.models.Sequential([
DenseTranspose(dense_2, activation="selu"),
DenseTranspose(dense_1, activation="sigmoid"),
keras.layers.Reshape([28, 28])
])
tied_ae = keras.models.Sequential([tied_encoder, tied_decoder])
- 이 모델은 절반의 파라미터로 이전 모델보다 약간 낮은 내구성 오차를 달성함
17. 3. 6. 한 번에 오토인코더 한 개씩 훈련하기
한 번에 전체 오토인코더를 훈련하지 않고, 오토인코더 하나를 훈련하고 이를 쌓아올려서 한 개의 적층 오토인코더를 만들 수 있음
- 현재는 많이 사용 x
- '탐욕적 방식의 층별 훈련' 에 대한 논문에 등장
- 단계 1 에서 첫 번째 오토인코더는 입력을 재구성하도록 학습
- 이 오토인코더를 사용해 전체 훈련 세트는 인코딩하여 (압축된) 새 훈련 세트를 만듦
- 새로운 훈련 세트에서 두 번째 오토인코더를 훈련 (단계 2)
- 마지막으로 모든 오토인코더를 사용해 전체 네트워크를 만듦 (단계 3, 각 오토인코더의 은닉층을 먼저 쌓고, 그 다음 출력층을 (반대로) 쌓음) -> 최종 적층 오토인코더
2006년 탐욕적 층별 접근 방법을 사용해 심층 신경망을 비지도 형태로 사전훈련될 수 있음을 발견 한 것이 딥러닝에 대한 큰 관심을 일으킴
-> 이를 위해 제한된 볼츠만 머신(RBM)을 사용했고, 오토인코더에도 적용된다는 것이 밝혀짐
- 11장에서 소개한 여러 기법으로 한 번에 심층 신경망을 훈련할 수 있게 되기 전까지는 이 방식이 심층 신경망을 효율적으로 훈련하는 유일한 방법이었음
오토인코더는 밀집 네트워크 외에도, 합성곱 오토인코더나 순환 오토인코더도 만들 수 있음
적층 오토인코더 - 모델 파라미터 초기화 (pre-training)
- 오토인코더가 많이 사용되던 2000년대 초반에는 (DNN 활성화 전), 차원 축소 외에도 pre-training에 많이 사용되었음(지금은 x)
- 오토인코더를 쌓아가며 학습해 pretrain을 시켜놓고 학습에 사용하는 방식
- 입력 데이터를 잘 표현하는 첫 번재 layer의 1000개에 대한 weight를 학습하고자함
- 입력 데이터를 복원하는 과정에서 데이터의 특징을 가지고 있는 weight를 학습하게 됨
-> 다시 말해, 적어도 입력 데이터에 대해서는 복원을 할 수 있는 weight를 얻고자 하는 것
- 다음 layer도 마찬가지로, weight를 복원하는 오토인코더를 구성하여 입력데이터를 잘 표현하도록 학습
- 학습이 끝나면, 오토인코더로 weight를 초기화해주고, 모델 학습을 위해 Backpropagation 진행
-> 오토인코더의 이런 특성으로 모델을 초기화하는 (pre-training) 용도로 많이 사용되었음
(오토인코더를 활용해, 입력 데이터의 특성을 나타내는 파라미터를 얻는 용도)
'AI > Hands-on ML' 카테고리의 다른 글
[핸즈온 머신러닝] 17장(4) - GAN, 생성적 적대 신경망 (0) | 2021.05.21 |
---|---|
[핸즈온 머신러닝] 17장(3) - 변이형 오토인코더, Variational AutoEncoder (0) | 2021.05.19 |
[핸즈온 머신러닝] 17장(2) - 합성곱 오토인코더, 순환 오토인코더, 잡음 제거 오토인코더, 희소 오토인코더 (0) | 2021.05.12 |
[핸즈온 머신러닝] 15장(2) - RNN과 CNN을 사용해 시퀀스 처리하기 (긴 시퀀스) (0) | 2021.04.27 |
[핸즈온 머신러닝] 15장(1) - RNN과 CNN을 사용해 시퀀스 처리하기 (0) | 2021.04.26 |