[회고] 2024 SSAFY 특화 프로젝트 - "코코디" 회고

insukL·2024년 4월 8일
0

회고

목록 보기
3/3
post-thumbnail

서론

SSAFY 2학기 동안 진행하는 2번째 프로젝트가 끝났다. SSAFY 내에서 통칭 특화 프로젝트라고 하는데, 2번째라 좀 익숙해질 줄 알았는데 1달이라는 짧은 시간에 만드는 건 적응이 되질 않는다.

프로젝트 시작

기술 선택

특화 프로젝트는 독특하게 인공지능이나 데이터, 핀테크와 같이 사용할 기술이나 도메인을 정하고 시작한다. 근래에 데이터를 수집, 저장하는 것에 관심이 많아지면서 분산 처리가 궁금해 데이터 분산 처리를 선택하자고 주장했다.

빗나가는 기획

그래서 정말 분산 처리를 선택하고, 기획하는 동안 분산 처리에 대해 찾아봤다. 하둡과 MongoDB를 선택하고 책도 사서 주말에 읽으며 어떻게 작업하면 좋을지 고민했다. 항상 새로운 기술을 익히면서 어떻게 사용해볼지 고민할 때가 가장 재밌다고 생각한다.

하지만 기획하면서 제일 문제는 분산 처리라는 기술을 어떻게 살릴지였는데, 기술을 정하고 기획하려니 너무 막막한 상황이 많았다. 그래서 기술을 후에 고려하고 만들고 싶은 기획을 해보기로 했다.

이후 기획한 내용은 '디지털 옷장 관리 및 코디 추천 서비스'였다. 사용자가 가진 옷을 사진을 찍어 등록하면, 이들의 조합에 대해 추천해주는 서비스를 만들기로 했다.

그 다음 서비스를 어떻게 만들 수 있을지 회의하니 데이터보단 AI에 가까워졌다. 사진을 찍어 인공지능 모델을 통해 옷을 인식하고, 이를 바탕으로 옷을 추천해주기로 했다. 그래서 내가 하고 싶던 분산 처리는 사라지고, 생각에도 없던 인공지능/데이터 업무가 넘어왔다.

정말 내 뜻대로 되는게 없었다. 프론트엔드와 백엔드 팀원이 미리 정해져 있기 때문에 백엔드로 다시 업무를 바꿀 순 없었다. 공부하지 않았던 일을 덜컥 맡게 되고, 당황스러움과 함께 프로젝트를 시작했다.

프로젝트 진행

이전까지 공부했던 내용이 필요 없어졌기 때문에 시작하자마자 사전 조사와 학습을 다시 진행했다. 1~2주 가량을 코드 없이 조사만 했던 기억이 난다.

될지 안될지 몰라요

우선 의류 데이터를 쌓는 동안 옆에서 AI 조사와 학습을 진행하던 팀원이 말했다. 의류 인식 및 검색까진 괜찮은데, 가진 옷의 조합을 판단하는 모델을 찾기 힘들다고 했다.

거기에 우리 서비스에 맞게 학습을 진행하기 위해선 어떤 조합이 어떤 사람에게 좋은 평가를 받았는가에 대한 정보가 필요했다. 이 데이터를 구하기 힘들고, 이를 통해 학습해도 우리가 가진 옷에 맞게 나올지 미지수였다. 1달 간의 짧은 프로젝트기 때문에 나올지 안나올지 모르는 결과에 목 매달 순 없었다.

2차 전직

이야기를 듣고 고심 끝에 선택한 방법은 내가 데이터 추천 쪽을 공부해서 의류 조합 추천 기능을 만드는 방법이었다.

다시 구글을 키고, 데이터 추천에는 무엇이 있는지, 어떻게 해야하는지 다시 고민을 시작했다.

데이터 추천 개발

기획 정리

사용자의 옷 목록이 주어지면, 이에 따른 옷 조합을 만들어야 했다. 예를 들어, 상의, 하의, 아우터, 신발과 같이 아이템을 모아 하나의 세트를 만들어야 했다.

협업 기반 필터링

처음 추천 방법에 대해 고민하면서 찾아보기 시작한 방법은 협업 기반 필터링이었다.

특히, 아이템 기반 협업 필터링을 진행해서 사용자의 옷장을 보고 비슷한 옷장을 가지고 있는 사용자에게 코디와 옷을 추천해줄 수 있지 않을까?라는 생각에서 시작했다.

이미지 출처 : 삼성 반도체 이야기

추천 모델의 경우엔 별도의 학습 없이 탐색을 진행하고, 라이브러리가 방대하다보니 아래의 글을 참고했다.

[Survey] 추천시스템 라이브러리 비교

해당 글을 보면서 다음의 생각을 가지고 모델 비교를 확인했다.

  1. 옷 자체의 특징을 반영해서 필터링을 해야 한다
  2. 사용자의 리뷰 데이터는 상대적으로 모으기 힘들다
    2-1. 설문으로 확보할 수 있지만, 시간이 부족하다
    2-2. 차후 사용자의 평가가 추가되면서 필터링에 영향을 주어야 한다

위 기준을 바탕으로 옷과 코디에 대한 직간접적인 평가를 사용할 수 있고, 하이브리드 방식으로 필터링이 가능LightFM을 사용해서 추천 모델을 사용해보려 했다.

그렇게 조사를 마치고 현재 시스템에 어떻게 적용할 수 있을지 고민하는 과정에서 몇 가지 문제점이 생각났다.

문제점 1. 아이템 간의 조합 추천

기본적으로 우리는 코디를 추천하기로 했다. 다시 말해서 옷의 조합을 추천해야 한다. 여기서 조금 LightFM을 적용하기 어려운 문제가 생겼다.

LightFM은 행렬 분해를 사용한다. 설명하자면 기존의 평가 행렬을 2개의 작은 행렬로 분해하고, 이를 다시 합하여 빈 칸에 예측하는 방법을 사용한다. 연산이 정확하지 않으나, 그림으로 흐름을 표현하자면 아래의 그림과 같이 설명할 수 있다.

이를 우리의 서비스에 적용하려고 하니, 카테고리 갯수에서 문제가 생겼다. 우리는 옷을 '상의', '하의', '아우터', '신발'이라는 4가지 카테고리로 분류하기로 했다. 단순히 상의와 하의가 어울리는지 비교가 가능하다. 하지만 카테고리가 4개가 되면서 연산이 복잡해졌다.

  1. 상의와 하의에 대해 행렬 분해를 실행해 좋은 조합을 구한다
  2. 상하의 조합과 아우터에 대해 행렬 분해를 실행해 좋은 조합을 구한다
  3. 상하의, 아우터 조합과 신발을 기준으로...

위와 같은 과정이 필요했고, 옷의 카테고리가 추가되거나 카테고리 내의 옷이 추가될 때마다 연산이 복잡해졌다. 거기에 부분 집합에 대한 평가가 필요하고, 카테고리 간의 선후관계가 생긴다는 문제가 있었다.

문제점 2. 피드백 적용

2번째 문제는 사용자의 피드백을 적용하기 힘들다는 점이다. 사용자가 우리에게 추천 받는 것은 코디다. 다시 말해서 사용자 입장에서는 옷 하나 하나에 대한 평가가 아니라, 조합된 코디가 어떤지 평가한다.

이를 적용하려는 협업 필터링에 적용하려면 사용자가 코디를 선택함에 따라 각각의 아이템에 대해 어떻게 평가를 적용할지 추가적인 고려가 필요했다.

특히 사용자마다 취향이 다른데, 똑같은 코디를 줬을 때 어떤 부분이 마음에 들어서 해당 코디를 선택하는지 우리가 파악해서 이를 평가에 반응할 방법이 없었다.

결과적으로 우리는 4가지 카테고리의 조합을 평가하기 때문에 LightFM을 적용해서 협업 기반 필터링을 적용하기 힘들다는 결론을 내렸다.

연관 분석

결국 다시 조사하고, 또 조사했다. 조사하면서 연관 분석에 대해 읽었는데, 룰 기반으로 아이템과 아이템 간의 어떤 관계가 있는지 파악한다는 점에서 우리 시스템에 적용할 수 이을 것 같아 더 조사했다.

Apriori 알고리즘

연관 분석 알고리즘에서 Apriori 알고리즘을 찾았다. Apriori 알고리즘은 단순하게 말하면 장바구니 추천처럼 현재 선택한 아이템들에 대해 어떤 아이템의 선택이 많았는지 알려줄 수 있었다.

정확하게는 아이템이 같이 선택되는 정도를 지지도라고 할때, 아이템의 조합을 구성하면서 일정 지지도의 아이템 셋에 대해서만 탐색해 연산량을 줄이는 알고리즘이다.

그림처럼 탐색하면 결과로 A, AB, AC, AD, ABC, ABD, ACD, ABCD의 아이템 셋을 구할 수 있다.

이를 바탕으로 아이템 셋을 만들 수 있으니, 사용자 옷장에서 포함되는 아이템 셋을 찾아 탐색을 하면 다른 사람이 많이 고르는 조합을 만들 수 있게 됐다.

데이터 수집

알고리즘 구현에 앞서 데이터를 수집하기로 했다. 의류 데이터는 많이 쌓여있는 무신사를 선택했다.

많은 데이터도 선정한 기준이지만 무신사에서 제공하는 가이드를 보고 데이터로 정했다. 실제로 우리가 의류 회사와 협업하면서 가이드를 정할 수 있으면 좋지만, 현재는 시간과 데이터가 없어 가능한 데이터를 사용해보기로 했다.

특히 너무 많은 데이터 수집은 서비스에 악영향을 줄 수 있다고 판단했기 때문에, 1분에 의류 하나씩 아주 천천히 수집했다. 1분에 하나씩 확인하니 그렇게 많은 데이터를 쌓을 순 없어 2~3일 간 기능 구현이 가능한 정도만 모았다.

사전 조사

Apriori 알고리즘도 결국 사용자가 선택한 이력을 바탕으로 빈도를 조사하기 때문에 조합에 대한 평가 내역이 필요했다. 그래서 단순히 버튼을 누르면 평가할 수 있는 페이지를 하나 만들어 팀 내에서 평가하기로 했다.

빠르게 개발해서 조사하기 위해서 간단한 페이지 하나를 개발했다. 물론 DB에 있는 옷 데이터를 가져와서 사진을 보여줘야 해서 API를 만들면서 페이지 자체를 서버에서 렌더링해서 보내줄 필요가 있었다.

빠른 개발과 간단한 수동 배포가 필요하니 Node를 선택해 사전 조사 페이지를 개발했다. 이전에 현장 실습하면서 Express를 사용해본 경험이 있으니 하루 정도 간단히 개발해 팀 내에 배포했다.

재밌었던 점은 간단하다곤 하지만 HTML 페이지 구성하기 싫어서 GPT에게 물어봤는데 만들어줬다.

처음에 다른 페이지를 만들어 줬는데, 다르게 디자인할 부분을 몇 번 짚어주니 위와 같은 페이지를 뚝딱 만들어줬다. 개발에 GPT를 사용하는 것을 썩 맘에 들어하는 편이 아니었는데, 이정도면 생각보다 꽤 괜찮게 쓸 수 있지 않을까.

성능 개선

Apriori 알고리즘의 가장 큰 문제는 트리를 구성하는데, 시간이 오래 걸린다는 점이다. 특히 우리 서비스를 생각하면 새로운 옷이 추가되거나 사용자가 옷을 등록하는 경우가 있는데, 이럴 경우 매번 새로운 트리를 생성하는 것이 매우 힘들다는 생각이 들었다.

옷 특징 추출하기

처음 생각한 개선안은 옷의 특징을 기반으로 트리를 구성하는 것이었다. 옷에는 색상, 두께, 계절감, 핏, 카테고리, 성별이라는 6개의 정보가 있다.

이 중 두께와 계절감은 패션보단 기온과 계절에 따른 착용에 따른 불편함에 관련되어 있다고 생각했다. 그래서 우선 두께와 계절에 따라 옷을 필터링하는 것에 사용했다. 기온을 0~28도를 기준으로 임의로 7단계로 분리했다. 이후 계절감과 두께의 포함 여부를 검사해서 입을 수 있는 옷을 필터링했다.

성별도 마찬가지로 입을 수 있는지에 대한 여부를 분류하고, 남은 특성은 색상, 핏, 카테고리로 나뉘었다. 색상이 약 40가지, 핏이 5가지, 카테고리는 상의, 하의, 원피스, 신발, 아우터 5가지로 분류되었다. 이를 언더바를 기준으로 합쳐서 옷의 특성을 나타내는 문자열을 만들었다. 이제 이 특성을 기준으로 조합에 대한 평가를 기록하고, 이를 Apriori 알고리즘에 사용했다.

이제 옷의 특징에 대해서 평가가 이뤄지므로 새로운 옷이 추가되어도 매번 트리를 만들 필요가 없고, 트리를 생성하는데 전체 옷에 대한 트리를 생성하는 것보다 적은 시간이 걸린다.

옷장 탐색하기

이제 아이템 셋을 매번 구성할 순 없지만, 옷의 특징을 기준으로 이뤄지면서 조합을 찾기 위해 매번 옷장을 탐색해야 하는 문제가 있었다.

순차 탐색을 계속 진행하면 옷의 갯수가 늘어날 때마다 조합을 완성하기 위한 탐색 횟수가 기하급수적으로 증가할테니 다른 방법이 필요했다.

그래서 map 자료구조, Python에선 dict 자료구조를 사용했다. 특징을 Key로 가지고 옷 리스트를 저장했는데, Python에서는 dict에 list형이 값이 변하기 때문에 바로 넣을 수가 없었다. 그래서 defaultdict를 사용해서 list형을 넣고 진행했다.

어떻게 보면 옷장을 정리하는 과정이 추가된 것이다. 하지만 조합을 탐색하기 위해 옷들을 여러 번 탐색해야 하고, 조합이 여러 개 요구되는 경우 이를 여러 번 탐색해야 해서 이렇게 구성해 시간을 많이 줄일 수 있었다.

아이템 셋 필터링

알고리즘을 통해 아이템 셋을 추출하는 것에 mlxtend 라이브러리를 사용했는데, 결과가 DataFrame으로 구성되어 조합을 입력하면 키를 통해서 금방 결과를 찾을 수 있었다.

여기서 착안하여 결과(추가되는 아이템)의 길이가 1인 결과만 필터링하여 전체 아이템 셋의 1/3가량으로 범위를 좁힐 수 있었다. 이제 Python의 DataFrame을 사용해서 특징 조합을 Key로 넣으면 다음 특징을 바로 찾을 수 있게 되었다.

여기서 아이템 셋의 결과가 독립적인지 아닌지는 큰 의미가 없고, 입력 아이템 셋과 결과 아이템이 얼마나 같이 있는지(=지지도) 중요하므로 support_only 옵션을 통해서 더 빠른 계산이 가능하게 했다.

pickle 저장

이렇게 만든 결과는 Python의 pickle 파일로 저장해 빠르게 저장하고, 빠르게 로드하여 DataFrame 그대로 사용할 수 있도록 했다. DB에 저장하는 것도 방법이었다. 하지만 추천 정보에 대해 버전을 유지할 필요가 없고, 최신 추천 아이템 셋만 유지하면 되기 때문에 오히려 DB와의 통신 시간이 더 느리다고 생각했다. 그래서 자체 파일 시스템을 바탕으로 pickle로 저장했다.

밤 새고, 밤 새고, 밤 새기

구현한 내용을 최대한 기록해두고 싶어서 히스토리를 기록하니 많이 길어졌다.

조사한 내용이 저렇고 여기에 실제로 프로그램 코드 작성이 남았다. 기획을 포함해서 4~5주다보니 개발 기간이 2~3주 가량 밖에 없어 마지막엔 밤을 좀 샜다.

프로젝트 마무리 하기

다행히 팀원이 RabbitMQ를 통해서 메시지를 통해 프로그램 동작이 가능하도록 구성해주어, 이를 연결하고 오류만 잡으면 됐다. 물론 그게 오래 걸려서 밤을 샜지만.

다른 기능도 많지만 이는 GitHub에서 확인할 수 있으니, 내가 개발한 옷장 기반 코디 추천과 쇼핑몰 의류 추천 기능만 넣어봤다.

후기

새로운 도메인의 도전

이번 프로젝트는 공부하던 서버도 아니고, 내가 하고 싶었던 데이터 분산 처리도 아니지만 Python으로 아예 새로운 기능을 만들어보는 기회가 되었다.

아직도 어엿한 Back-end 개발자라고 하기 힘들겠지만, 그래도 CRUD 위주의 프로젝트가 4~5개는 쌓이면서 새로 프로젝트를 해도 똑같이 진행되지 않을까 생각을 했다. 그런데 이번에 아예 새로운 방식으로 하지 않았던 기능을 개발하면서 어떻게 새로운 개념을 익혀서 사용하는지 환기하는 경험이 된 것 같다.

데이터 사이언스로 넘어가는 것은 없던 생각도 쏙 들어갔지만, 환기된 경험으로 기존 서버 개발도 어떻게 잘 개발할지 고민하면서 시도해봐야 겠다고 생각한다.

데이터 부족

데이터에 따른 추천을 해보면서 제일 큰 문제는 데이터의 부족이었다. 실제로 프로젝트를 완성했음에도 필터링 과정에서 적절한 데이터가 없어 조합할 데이터가 없어 에러가 나는 경우가 잦았다. 그에 반해 사전 조사에선 기온이나 성별에 대한 필터링은 있으나, 수집한 데이터를 모두 사용하다보니 랜덤으로 배치했음에도 100% 괜찮지는 않아도 조합을 확인하다보면 생각보다 괜찮은 결과도 나왔다.

그래서 꽤 많은 데이터를 모았다면, 더 정확하고 재밌는 결과를 만들 수 있었지 않을까하는 후회가 있다. 물론 서비스에 영향을 덜 끼치기 위해 적은 데이터를 모았다고 하지만 아쉬운건 사실이다.

데이터를 다루는 공부를 해보고 싶다고 말만 했지 실제로 데이터가 많이 수집하는 것은 굉장히 어려웠다. 상담에서 이런 점을 얘기도 해봤는데, 공공 데이터와 적당한 스크래핑이 답변으로 돌아와서 참 애매하다는 생각이 들었다.

개념과 수단의 분리

Python으로 진행하면서 제일 부족했던 부분은 예외 처리였다. 비록 어플리케이션 서버를 구성하는 것이 아니었지만 엄연히 실시간 서비스를 가정하고 구성했으니, 이에 따른 예외 상황을 처리하는 것이 필요했다. 대표적으로 사용자가 등록해둔 옷이 없거나, 필터링 시 적합한 옷을 찾을 수 없는 경우가 그랬다. 그래서 예외가 발생할 때마다 수정할 수 밖에 없었고, 코드 품질이 낮아지는 결과로 이어졌다.

프로젝트를 마무리하고 다음날 곰곰히 생각해봤는데, 결국은 개념과 수단의 분리가 덜 이뤄졌다고 생각했다. Spring을 사용해서 진행할 땐 잘만 처리하던 예외 상황을 Python으로 진행했다고 못잡는다? 이는 그냥 예외를 어떻게 처리할지 Spring이라는 수단에 종속되어서 그렇다고 생각한다. 앞으로는 어떤 개념을 익혀도 수단에 종속적인 특징이 아니라면 이를 다른 수단에 적용할 수 있도록 노력해야겠다.

다음 목표

다시 서버로

아직 SSAFY에서 진행할 수 있는 프로젝트가 1번 더 남았는데, 이번엔 서버 어플리케이션 개발을 맡고 싶다. 특히 정합성이나 캐싱 등 서비스를 고도화할 수 있는 프로젝트를 진행해보고 싶다.

특히 동시성 문제와 같이 CRUD에서도 충분히 발생할 수 있는 상황에 대해 공부해서 이를 해결해보고 싶다. 내가 모르는 것이 너무나 많다.

미뤄둔 공부

마지막에 툭하면 밤을 새면서 작업을 진행하다보니 체력이 너무 부족했다. 그래서 블로깅하는 양도 줄어들고, 익히고 싶어서 공부한 내용도 적었다. 다음 프로젝트 초기에 기획/설계 기간에 미뤄둔 공부를 좀 더 하고 싶다.

데이터베이스

저번 회고에서도 작성했는데, 데이터베이스 공부를 더 하고 싶다. 원래 이번에 분산 처리를 공부하면서 Hadoop을, 그리고 이걸 저장할 수 있는 DB 구조 설계와 가능하면 NoSQL 공부까지 해보고 싶었다. 하지만 데이터 추천으로 업무가 바뀌면서 목표로 했던 공부를 못했다.

막상 다른 DB로 milvus라는 Vector DB를 사용했는데, AI를 통한 이미지 검색을 위해 DB가 변경되면서 2주도 안남기고 진행하다보니 제대로 공부하지도 못하고 적용해서 이도 저도 되지 못한 것이 아쉽다.

몽고 DB 가이드를 읽다 말아서, 이를 계속 읽어보고 싶다. 그리고 이전에 언급한 책들도 읽어서 정리하고, Real MySQL8.0도 읽어보고 싶은데 읽을 책이 너무 많다.

JPA

JPA 책을 읽어서 블로깅하는 목표가 있는데, 아쉽게 1/4정도만 적고 한 2/3 정도 읽었는데 더는 진행을 못하고 있다. 이를 다 정리해서 블로깅해두는 것이 좋을 것 같다.

아키텍처

이번에 추천 기능을 다루면서 같이 보진 못했는데, SSE나 Message Queue를 적용한 것을 봤다. 저런 아키텍처를 구성하는 공부를 해보고 싶다. 개념부터 정리해두고 아키텍처 설계에 활용해보고 싶어, 미뤄둔 공부를 빨리 마무리하고 아키텍처 관련으로도 공부를 진행해보고 싶다.

취업

새로운 기술을 익히고, 새로운 프로젝트를 진행해보는 것은 언제나 즐겁다. 하지만 나 혼자서는 한계가 존재하고, 살아가는덴 직업이 필요하다.

그래도 이왕이면 나랑 잘 맞고, 내가 성장할 수 있는 곳을 찾고 싶다. 너무 동화 속 이야기라고 할 수 있지만, 꼭 찾을 수 있다고 생각한다. 그러니까 다음 프로젝트도 노력하고, 취업 준비도 노력해야겠다.

profile
데이터를 소중히 여기는 개발자가 되고 싶습니다

0개의 댓글