[프로그래머스 AI 데브코스] Monthly project 2 - 로지스틱 회귀분석과 knn 알고리즘을 활용한 scene 이미지 분류
프로적트 목표 : 이미지 분류
팀 원 : 김승호, 김태형, 신은채, 이승은, 정찬비, 조현근
프로젝트 기간 : 23 / 05 / 03 13:00 ~19-30
0. 개 요
프로그래머스 2번째 팀 프로젝트로 케글 컴페티션과 결합한 첫 딥러닝 프로젝트를 진행하였다.
데이터와 전반적인 프로세스는 유투브 활동도 하시고 현업 AI 연구원으로 계시는 '나동빈' 강사님이 준비해주셨으며, 최종 발표 평가도 진행해주셨다.
데이터셋은 scene 이미지 분류 문제로, 산, 들판, 빌딩 등의 장소와 관련된 사진을 가지고 각 class에 분류하는 컴페티션이며 이미지 분류의 가장 기본적인 데이터셋 느낌이였다. 이미지분석 경험이 많지 않던 나는 긴장이 좀 됐지만, 나동빈 강사님이 구성하신 커리큘럼에 따라 불필요하게 시간이 투자되는 코드들은 틀이 어느정도 짜여 있었고, 이미지 분류에 강점이 있던 팀원분이 전처리를 빠르게 진행해 주셔서, 획보된 시간속에서 다양한 시도들을 해볼 수 있었던것 같다.
이제껏 배웠던 여러 방법론들을 실제로 적용해보면서, 배운 이론들이 실제로 현업에서 어떻게 작동하고 어떤 효과를 내는지 다양하게 경험해볼 수 있었던 좋은 기회였다고 생각한다.
결과적으로 "전처리-모델설계-모델학습-모델성능-결과해석-ppt 작성 및 발표" 로 이어지는 컴페티션 과정에서, 성능은 다른조에 살짝 밀려 2등을 차지했지만, 전반적인 아키텍쳐 구성이 합리적이고 세련되었다는 과분한 칭찬과 함께 최종 점수로 1등의 성적을 거두었다😀
- 컴페티션 데이터셋
https://www.kaggle.com/datasets/nitishabharathi/scene-classification
Scene Classification
Contains ~25K images from a wide range of natural scenes
www.kaggle.com
- 캐글 노트북
https://colab.research.google.com/drive/1VRaH_tgAd7dZjCnlZy8uEeZAldeyfzKI#scrollTo=ZvISX94Ma8AB
Google Colaboratory Notebook
Run, share, and edit Python notebooks
colab.research.google.com
- 발표자료 PPT
1. 전처리
데이터셋은 총 24,335개의 이미지가 존재했으나, 우리는 캐글 제출이 아닌 기존의 train set만 사용하고자 했으므로 17,034 개의 데이터를 최종적으로 사용할 수 있었다. 데이터는 총 6개의 클래스가 존재했다.
0. 빌딩(buildings)
1. 숲(forests)
2. 빙하(glacier)
3. 산(mountains)
4. 바다(sea)
5. 거리(street)
각 이미지는 150X150X3 픽셀로 구성되어 있었으며, 이를 그대로 사용할 경우 변수가 약 67,500개나 되었으나, 이를 로지스틱 회귀모형으로 학습하기에 예측해야되는 변수가 너무 많아지므로 데이터 로드 과정에서 64X64X3 의 크기로 통일해서 resize를 진행하였다.

image_resize를 통해서 해상도를 줄이더라도 여전히 12,288 개의 픽셀 데이터가 input으로 들어왔고, 이것을 validation set을 제외한 train dataset 만을 봤을 때 13,627개의 데이터셋만으로 logistic regression을 학습시키기 힘들다고 판단했다. 실제로, 예시코드로 주어진 scikit learn의 기본적인 logistic regression 의 파라미터 조정으로는 accuracy가 49% ~ 53% 정도를 웃돌았다.
그래서 우리는 성능 향상을 기대하기 위해, 크게 두가지의 전처리과정을 고려했다 했다.
1. Data Augmentation (데이터 증진)
2. PCA (차원 축소)
1-1. Data Augmentation (데이터 증진)
가지고 있는 이미지를 활용해서 데이터를 증진시키는 방식이 있다. 대게는 이미지를 상하 좌우로 뒤집거나(Flip), 상하좌우로 이미지를 살짝 밀어주거나(shift), 이미지를 살짝 회전시키거나(Rotate), 노이즈를 추가하는 방식(noise)을 주로 쓴다.
여기서 자세히 들어가 보면, 이미지 shift나 rotate의 경우, 원본 이미지를 밀어낸 빈 공간이 생기는데, 이를 채워주는 방식이라던가, 이미지를 얼마나 이동시킬것인가에 대한 것들도 하이퍼파라미터 조정을 통해서 선택할 수 있고, noise 방식도 가우시안 노이즈, solt and peper noise 등 다양하게 고려햘 수 있지만 우리는 그 중에서 sift와 horizontal Flip만을 활용하여 이미지를 원본 사이즈의 3배로 늘리기로 결정했다.
1-2. PCA (차원 축소)
데이터셋에 대해 차원축소를 고려했다. 특히 전통통계모형에 대해서, 이미지 데이터셋은 크게 두가지의 문제점이 있을 수 있는데, 일단 데이터가 갖는 변수에 비해 데이터 수집량이 매우 적다는 것이 문제이며, 각 픽셀 사이의 상관관계가 존재할 가능성이 크므로(ex 주로 하늘배경인 위쪽 모서리, 주로 어두운 그림자가 진 아래쪽 모서리) 점진적인 방법으로 해를 찾더라도 해가 잘 구해지지 않는 문제가 발생할 수 있다. (다중공선성 문제)
이러한 상황에서 PCA는 선형젹인 효율적 정보 축소를 통해서 중요변수를 추려주며, 각 주성분끼리는 수직관계이므로 변수간의 상관관계도 해소할 수 있는 방법론이다.
우리는 주성분 크기 비율을 통한 누적정보량을 확인해 초기 주성분을 설정하고, 성능평가지표를 기준으로 그리드 서치와 조원 5인의 인력을 통한(?) 병렬 연산을 통해서 최적의 주성분 갯수를 탐색하고자 하였다.
2. Result of Logistic Regression
로지스틱 분류기는 가장 전통적인 통계적 분류 모델이다. 로지스틱 분류는 주어진 데이터를 활용하여, 이를 가장 잘 구분해주는 로지스틱 함수를 적합해주어 이러한 이진분류 문제에서 각 클래스에 속할 확률을 예측하는 방식이다.
여기서 우리 이미지는 이진분류가 아닌 총 6가지 클래스를 가지는데, 그렇다고 방법론이 완전 바뀌는건 아니고, 한 클래스에대해 속할지 안 속할지에 대한 로지스틱 회귀식을 여러개 세워서 총 6가지로 분류한다고 생각하면 된다.
로지스틱 회귀분석의 실질적인 성능을 높이기 위해서, 우리는 다양한 하이퍼파라미터 세팅을 시도했다. 그중 크게 4가지를 고려했는데, penalty, solver, max_iter, C 이다.
일단 변수의 양이 너무 많다보니, 학습시간이 오래걸렸고, 그에 따라 solver의 선택이 모델학습시간을 매우 좌우했다. penalty 역시 solver에 따라 사용할 수 있는 종류가 제한이 있었고, 우리는 다양한 penalty 를 선택하기 위해 그에맞는 solver를 찾아보며 다양한 시도들을 했다.
나는 변수의 양을 줄이는 기능도 수행하는 l1규제가 실질적으로 학습에 도움을 줄 것으로 생각했으나, 오히려 validation accuracy는 조금 떨어지는 성능을 보였고, 이외에도 다양한 규제들을 적용해 가며 validation accuracy를 비교해 갔으며 최종적으로 아래와 같은 파라미터 세팅을 진행하였다. (참고로 pca를 진행했을 때 이보다 더 좋은 성능을 냈던 모델도 있었으나, 자료를 취합하는 과정에서 빠졌던것 같다.)
- penalty : L2
- solver : lbfgs
- max_iter : 30
- C : 100
로지스틱 회귀분석을 적합하면서 느낀점은, 여러 방법론들을 고민하고 적용해도 실질적으로 성능을 1%, 0.1% 올리는게 쉽지 않음을 알 수 있었고, 일반적으로 생각했던 하이퍼파라미터 세팅에 대한 직관 역시 실질적으로 성능평가를 진행하면서 의외의 결과를 많이 볼 수 있었던 결과가 아니였나 싶다.
3. Result of KNN
이번 장에 사용하고자 하는 knn 알고리즘 역시 머신러닝에서 가장 간단한 형태의 분류기라고 볼 수 있다. 우리가 학습데이터에 대해 모든 label 을 알고있다는 가정 하에, 새로운 점(우리의 validation data) 이 들어왔을때, 모든 학습데이터의 점과의 길이를 계산하고, 그중에 제일 가까운 k개만의 거리를 남겨놓는다. 그리고 그 k 개의 가장 인접한 점들이 어느 클래스에 가장 많이 속해있는지에 따라서, 새로운 데이터에 대한 라벨을 붙이는 방식이다.
문제에서 주어진 목표는 다음과 같았다. l1 distance, l2 distance가 기본적으로 주어져 있고, 이 외에도 다른 distance 함수를 구현하거나 소스코드를 변형하여 knn 분류기 성능을 높이기.
일단 우리는 현실적으로 다음과 같은 문제점을 파악했다.
일단 계산 비용의 문제이다. 이미지 데이터는 한장당 64*64*3 = 약 1만 2천개의 변수를 가지고 있으며, 이는 train 데이터셋이 총 1만 3천장으로 구성되어 있으므로, 테스트 데이터 한장당 거리 연산량은 약 1억회, 여기에 이를 정렬하는 연산량이 들어간다. 즉 원본 데이터를 그대로 사용할 경우, 연산량문제와 메모리문제가 따라올 수 있다.
두번째로 변수간의 분산 차이를 고려해야한다는 것이다. 이미지데이터 특성상 특정 위치의 픽셀은 각 이미지에 있어 크게 변화가 없을 수도 있고(주로 하늘이 찍히는 이미지 상단, 그림자가 자주 지는 이미지 하단) 반대로 이미지의 중심부는 각 클래스마다 색차이가 뚜렷할 것이다. 이러한 변수간의 분산 차이를 고려해 진행하는것이 거리 계산에서 유리할것이라고 판단했다.
그래서 우리는 아래와 같은 프로세스를 통해 이를 극복하고자 하였다.
- StandardScaler를 통한 변수 표준화 진행
- PCA 를 통한 차원 축소
- 데이터의 분산까지 고려해주는 여러 Distance matrix 를 고려
- Grid search를 통한 최적의 거리(dist), 파라미터(k) 탐색
그래서 우리는 기존에 l1, l2 distance만으로 구현되어 있는 class 객체 kNearestNeighbors(object) 에 Mahalanobis_distance, cosine_sim, standardized_distance, Minkowski_distance 를 추가적으로 구현하였고 위와 같은 프로세스를 전반적으로 진행하였다.
4. Trouble Shooting
마지막으로 전반적인 프로세스 과정에서 겪었던 어려움과 개선점에 대해 짚고 넘어가고자 한다.
일단 첫번째로 Mahalanobis_distance 를 구현하는데 있었던 문제에 대해 짚고넘어가자면, 마할라노비스 계산식은 아래와 같다. 이를 모든 X_train에 대해 한방에 행렬연산으로 계산해야 하므로 x 자리에 모든 X_train 의 값들이 들어가다 보니 메모리에 올리자 마자 세션이 날아가는 문제가 발생했다.
이에 대한 해결책으로, 텐서플로우나 파이토치에 구현되어있는 knn을 사용함으로서 병렬연산을 사용해도 되고, 배치를 나누어서 한번에 계산되는 계산량을 분할하는 방법론이 있을 수 있다고 생각한다.
그리고 전반적으로 Logistic Regression 이나 KNN 모두 픽셀 전체에 대한 연산이 계산되다 보니 모델상의 한계로 학습이 오래 걸리고 메모리 문제가 발생하는 경우가 많았다. 이는 컴페티션에 캐글을 이용했던 나에게 크리티컬하게 작용했는데, 모델을 한번 돌릴때 마다 메모리 문제를 신경써주지 않으면 세션이 계속해서 날아갔다.
그래서 우리는 전반적으로 PCA라는 차원 축소 방법론을 고려했고, 실질적으로 이를통해 학습속도 향상과 성능 두가지의 문제를 해결하였지만, 조금 더 나아가서 이보다 효과적인 방법론들을 활용해볼 수 있으면 더 성능을 크게 높일 수 있지 않았을까 싶다.