강남우 교수님의 youtube 강의를 수강한 기록이다. 오토인코더 구조는 사용할 일이 많아 잘 알고 있었으나, 이를 이상 탐지에 사용할 수 있다는 점이 흥미로워 강의를 들어보았고, 매우 간단하나, 재미있어보여서 mnist 데이터로 실제 구현도 해보았다.
1. Autoencoder
- 오토인코더는 인코더와 디코더로 이루어진, 입출력이 동일한 모델 구조를 말함
- 인코더의 출력이자 디코더의 입력을 z, latent vector라 하며, 주로 입력 데이터의 크기보다 작게 구성함. 다른 말로는 code, feature, hidden representation이라고도 함
- 학습을 거듭하면, 입력 데이터는 인코더를 거쳐 latent vector로 표현되고, 이 벡터가 디코더를 거쳐 입력 데이터로 복원됨
- 데이터를 압축하는 구조이기 때문에, 주로 차원축소를 위해 사용됨
오토인코더는 수식으로 다음과 같이 나타낼 수 있음
2. 오토 인코더의 응용
- 차원 축소: 학습 이후 인코더만 사용하여 x → z로 차원축소함
- transfer learning: encoder → decoder 학습시킨 뒤, 인코더에 classification layer 등을 붙여 fine-tuning
- anomaly detection: 이상탐지, 학습 데이터에 정상만 포함하여 학습. 인퍼런스 시 정상의 reconstruction error 대비 이상의 에러가 클 것. 보통 여기에 threshold를 적용하여 일정 수준 이상의 오류는 이상데이터로 본다.
3. 분류 문제의 매트릭
Confusion matrix와 acc, pr, recall
- confusion matrix
- 질병의 진단을 예시로 들 때, 정상을 negative, 질병을 positive로 둠
- FP, type 1 error 라고도 함. 정상인데 질병으로 예측한 경우
- FN, type 2 error 라고도 함. 질병인데 정상으로 예측한 경우임. 이 예제에서는 매우 크리티컬한 이슈임.
- acc는 전체 중에 예측결과와 정답이 일치하는 경우를 백분율로 나타낸 것
- precision은 TP/(TP+FP)로 예측 결과가 positive(질병)라고 한 것 중 실제 질병의 수를 백분율로 나타냄
- recall은 TP/ (TP+FN)으로 실제 질병들 중 예측 결과도 질병인 것의 수를 백분율로 나타낸 것
4. 오토인코더 기반 이상 탐지 구현
데이터로는 간단하게 불러올 수 있는 mnist를 사용함. 전체 코드는 아래 ipynb 파일을 다운받아 확인할 수 있고, mnist 데이터 다운로드와 python으로 읽는 방법에 대해서는 다음 포스팅을 참고하면 된다. 아래에서는 핵심적인 코드 조각만 가져와 설명한다.
mnist로 기획한 이상 탐지 문제
- 숫자 1은 정상 데이터이고, 나머지 숫자는 다 이상데이터이다.
- 숫자 1 데이터에 대해서만 Autoencoder를 학습한다.
- 테스트 데이터로 숫자 1의 loss 값과, 나머지 숫자의 loss를 비교해 이상 값으로 여겨질만큼 큰 차이가 있는지 확인해본다.
- 이 때, 숫자 7은 숫자 1과 나름 유사하므로, 다른 숫자들에 비해서 loss 값이 적게 차이나는지도 확인해본다.
- 결론적으로 loss에 임계치를 적용하면 이상 탐지가 될지 고찰해본다.
train_image와 test_image를 각각 아래와 같이 1, 7, 나머지의 세 종류로 나눈다.
train_images_1, train_images_7, train_images_rest = [], [], []
for img, label in zip(train_images, train_labels):
if label == 1:
train_images_1.append(img)
elif label == 7:
train_images_7.append(img)
else:
train_images_rest.append(img)
train_images_1, train_images_7, train_images_rest = np.array(train_images_1), np.array(train_images_7), np.array(train_images_rest)
train_images_1.shape, train_images_7.shape, train_images_rest.shape
# Output
## ((6742, 784), (6265, 784), (46993, 784))
오토 인코더 모델을 빌드한다
def get_ae(input_size, latent_size):
input_tensor = tf.keras.layers.Input(shape=input_size, dtype='float32')
encoder = tf.keras.layers.Dense(units=latent_size, dtype='float32', name='encoder', activation='relu')(input_tensor)
decoder = tf.keras.layers.Dense(units=input_size, dtype='float32', name='decoder', activation='sigmoid')(encoder)
return tf.keras.Model(input_tensor, decoder)
ae = get_ae(28*28, 100)
ae.compile(optimizer='adam', loss='mse')
오토 인코더 모델을 숫자 1 데이터에 대해서만 학습한다 (입출력이 동일)
history = ae.fit(train_images_1, train_images_1,
validation_split=.2, epochs=100, batch_size=125)
숫자 1, 숫자 7, 나머지 숫자의 데이터에 대해서 loss를 구해본다
숫자 1에 대한 결과
- 학습 데이터 loss: 0.0009
- 테스트 데이터 loss: 0.0011
ae.evaluate(train_images_1, train_images_1), ae.evaluate(test_images_1, test_images_1)
# Output
## (0.0008762814686633646, 0.0011027402943000197)
숫자 7에 대한 결과
- 학습 데이터 loss: 0.0325
- 테스트 데이터 loss: 0.0310
ae.evaluate(train_images_7, train_images_7), ae.evaluate(test_images_7, test_images_7)
# Output
## (0.032452795654535294, 0.03099246881902218)
나머지 숫자에 대한 결과
- 학습 데이터 loss: 0.0423
- 테스트 데이터 loss: 0.0425
ae.evaluate(train_images_rest, train_images_rest), ae.evaluate(test_images_rest, test_images_rest)
# Output
## (0.04227061569690704, 0.04254015535116196)
위 결과를 통해 임계치를 약 0.01 정도로 설정하면, 숫자 1이 아닌 데이터에 대해서 충분히 이상 탐지가 가능할 것으로 보임. 단, mnist 데이터는 매우 명쾌한 차이를 보이는 데이터 세트인데, 실제 공장의 불량 등에는 이상 여부를 판단하는 기준이 매우 strict 할 것으로 보여, 더 깊은 층으로 모델을 구성하는 등의 작업이 필요할 것으로 생각된다.
전체 코드를 담은 스크립트 (tensorflow 2.10 으로 구현)
마무리
일단 강의가 명쾌하고 재미지다. 약 3년 정도 된 강의같은데 출퇴근 시에 조금씩 듣고 정리/구현하면서 개념을 리마인드하는 데에 매우 도움이 될 것 같다.
'인공지능' 카테고리의 다른 글
tf.matmul과 tf.multiply 차이 (행렬곱, 성분곱) (0) | 2023.07.18 |
---|---|
[Kaggle] A Journey of Enzyme Classification (0) | 2023.07.17 |
MNIST 데이터 읽기 (0) | 2023.07.03 |
multi-label classification의 활성화 함수와 손실 함수 (0) | 2023.07.01 |
LlamaIndex로 검색 엔진 구축하기 (라마인덱스, openai-cookbook) (1) | 2023.06.29 |