수다뒤
목차
팀 정보 #
- 김선규(BE) : https://seonkyukim.github.io/about/
- 정재홍(FE, ML serving) : https://jaehong21.com/
- 이준명(ML) : https://slime0519.github.io/
- 제갈윤(BE, ML serving) : https://yunjegal.notion.site/Yun-Jegal-a1aab6ba45574b2e8f058b5f382bfabb
서비스 소개 #
‘수다뒤’ 서비스는 한국의 고등학생들이 수학을 학습하는 데 도움을 주기 위해 개발되었습니다. 한국의 고등학교 수학 교육은 대수, 기하 등 다양한 주제를 다루며, 여러 유형의 문제가 포함되어 있습니다. 따라서 학생들이 빠르게 학습 진도를 나가기는 쉽지 않습니다. 그래서 저희 팀은 학생들이 낯선 문제를 캡처하면 비슷한 문제를 추천하여 약한 부분을 신속하게 개선할 수 있도록 돕는 서비스를 개발했습니다.
시스템 디자인 #
비슷한 문제를 추천하기 위해, 저희 팀은 임베딩 기반의 유사성 검색 방식을 활용하였습니다. 이 방법은 보이지 않는 데이터를 처리하는 데 강력하며 추론 시간에 적은 계산 비용을 필요로 합니다. 또한, 확장성과 속도 최적화가 필요할 경우, 벡터 검색 프레임워크인 faiss를 벡터 인덱스에 추가할 수 있습니다.
이 시스템은 GPU 인스턴스 없이 CPU 추론만을 사용하여, AWS 람다 인스턴스만을 사용하는 저비용 시스템을 구축할 수 있었습니다.
![[system-design.png]]
위의 시퀀스는 real-time으로 발생합니다
- 사용자가 수학 문제의 사진을 찍어 백엔드 API에 전달합니다.
- 백엔드 API는 수학 문제 이미지를 라텍스트 문자열로 변환하기 위해 MathPix API를 호출합니다.
- 백엔드 API는 변환된 라텍스트 문자열로부터 임베딩을 얻기 위해 추론 API 요청을 합니다.
- S3에 저장된 기존 문제 세트에서 임베딩 유사성을 사용하여 가장 유사한 3개의 수학 문제를 선택하여 사용자에게 응답합니다.
1. 프론트엔드 #
- Flutter (Dart 언어) 안드로이드 & iOS 모두를 위한 앱 프레임워크
2. 백엔드 #
- 모델 추론 API
- BentoML을 통한 ML 모델 패키징
- AWS 람다 + API 게이트웨이를 통한 서빙
- Python + FastAPI
- 백엔드 API
- AWS 람다 + API 게이트웨이를 통한 서빙
- Python + FastAPI
- 데이터 레이크 & DB
- AWS S3
3. 데이터 파이프라인 #
시간 제한과 데이터 품질을 고려하여, EBS에서 배포된 “수능특강” 워크북에서 수학 문제를 추출하기로 결정하였습니다. 워크북이 PDF 형식이며 스캔된 복사본이 아니기 때문에 다양한 이미지 노이즈가 포함되어 있지 않으며, 메타데이터를 사용하여 데이터를 신속하게 수집할 수 있습니다. 따라서 “수능특강"을 데이터 소스로 선택하였습니다.
답안-문제 pair 수집 과정:
- 답안지와 질문지에 대한 경계 상자를 생성하기 위해 사전 훈련된 YOLO v5 모델을 적용합니다.
- 부정확하게 감지된 경계 상자를 식별하고 수정하기 위해 수동 검사를 실시합니다.
- 답안지 PDF에서 추출된 텍스트 메타데이터와 경계 상자를 사용하여 각 경계 상자에 해당하는 문제에 레이블을 지정합니다.
- “LabelImg” 도구를 사용하여 답안지에 할당된 문제 레이블의 정확성을 검증합니다.
- 답안지에서 파생된 문제 세트를 질문지의 경계 상자와 정렬합니다.
- “LabelImg” 도구를 사용하여 질문지에 할당된 문제 레이블의 정확성을 검증합니다.
- Mathpix API를 활용하여 얻은 경계 상자에서 문제들을 추출하고 LaTeX 문자열로 변환합니다.
초기 YOLO v5 모델은 약 200개의 수동 레이블이 지정된 데이터 샘플을 사용하여 훈련되었습니다. 반자동 데이터 수집 파이프라인을 통해 한 해 동안의 워크북에 대한 데이터 수집 시간이 150분에서 15분으로 줄어들어, 효율이 약 10배 향상되었습니다.
머신러닝 컴포넌트 #
저희 제품에서 모델은 문제를 벡터 임베딩으로 변환하는 역할을 합니다. LaTeX 문자열로 변환된 문장을 임베딩하기 위해, 저희는 KLUE NLI/STS 데이터셋에서 사전 훈련된 sRoBERTa 모델을 미세 조정하였습니다. 이 미세 조정된 모델을 사용하여 문제를 768차원의 벡터로 임베딩할 수 있습니다.
실험 1. Score-based Regression (점수 기반 회귀) #
STS(의미적 텍스트 유사성) 작업과 유사한 접근 방식으로, 저희는 문장 임베딩 쌍에 점수를 부여하여 임베딩 벡터 간의 코사인 유사도를 비슷하게 만드는 모델을 훈련시켰습니다. 구체적으로, 문제의 난이도와 소주제가 같을 때 더 높은 유사성 점수를 부여하는 방식으로 점수 메커니즘을 설정했습니다. 이 접근법은 같은 난이도와 소주제를 가진 문제들이 높은 유사성 평가를 받도록 보장합니다.
점수 기반 손실로 미세 조정된 모델은 기하 문제 데이터셋에서 사전 훈련된 모델보다 훨씬 더 나은 결과를 보여주었습니다. 이는 아래의 t-SNE 임베딩 결과에서도 관찰할 수 있습니다.
실험 2. Contrastive Loss (대조 손실) #
점수 기반 손실만으로는 미적분과 기하와 같은 큰 문제 도메인의 의미 정보를 포착하는 데 실패했습니다. 대신, 문장 구조와 사용된 단어에 대한 표면적 정보를 인코딩하는 경향이 있었습니다. 이러한 한계를 극복하기 위해, 저희는 챕터에 대한 대조 손실을 도입했습니다.
대조 손실을 도입함으로써, 저희는 직접적인 부정 피드백을 제공할 수 있었고, 점수 기반 손실만을 사용할 때보다 더 나은 추천 성능을 달성했습니다. 이 개선 사항은 시각화에서도 명확히 확인할 수 있습니다.
아래 그림은 대조 손실 추가로 훈련된 결과를 나타냅니다. 모델의 임베딩이 미적분과 기하 관련 챕터 간에 더 명확한 구분을 보여 주는 것을 관찰할 수 있습니다. 이 구분은 특히 “여러 가지 함수의 미분” 문제 세트에서 두드러집니다. 그러나 이전 접근 방식과 비교하여 일부 클러스터 내에서 응집력이 감소하는 경향이 있습니다. 이는 향후 반복에서 개선할 수 있는 영역입니다.
실험 3. OpenAI를 통한 단원 및 난이도 분류 #
저희 팀은 문제의 단원과 난이도를 분류하기 위해 OpenAI API로 모델을 미세 조정했습니다. 프롬프트와 해당 클래스를 위한 디자인을 고려해야 합니다. 프롬프트의 끝을 나타내는 구분자를 추가하고, 동일한 구분자를 추론에도 사용합니다. 또한, 클래스의 토큰 길이는 1이어야 하므로, 위에서 얻은 레이블을 직접 사용할 수 없습니다. 대신 각 레이블은 1의 토큰 길이를 가진 정수로 지정되어 훈련에 사용됩니다. 정확도를 측정하기 위해, 훈련 데이터와 검증 데이터를 나누어 미세 조정해야 했지만, 수집된 데이터가 제한적이므로 별도의 검증 세트는 만들어지지 않았습니다. 훈련 비용과 시간이 현저히 낮기 때문에, 훈련 후 추론 결과를 직접 검토하여 불일치가 발견되면 추가 데이터를 수집하고 모델을 다시 훈련했습니다.
시스템 평가 #
전체 응답 시간 #
평균 백엔드 API의 응답 시간은 3~4초입니다. 백엔드 서버는 추론 서버에 API 요청을 보내고, 임베딩, 수준, 단위의 세 가지 API 호출을 합니다. 이 과정은 응답 시간에 큰 영향을 미쳤으며, 모든 응답을 한 번에 받도록 설계했다면 사용자 경험이 더 좋았을 것입니다.
모델 API 응답 시간 #
성능 테스트는 1, 10, 20명의 동접자를 가정해서 실행되었습니다.
모델 인프라는 1, 10, 20명의 사용자와 함께 테스트되었습니다. 단일 사용자는 초당 3.3개의 요청(RPS)을 보낼 수 있습니다. 1, 10, 20명의 사용자에 대한 수렴된 RPS는 각각 3.3, 39, 77로 사용자 수의 증가에 비례하여 증가하며, 모든 경우에 실패 응답은 발견되지 않았습니다. 사용자 수에 관계없이 응답 시간은 50백분위에서 대부분 0.25초, 95백분위에서 0.34초로 일정했습니다. 그러나 응답 시간이 6초에 이르는 경우가 간혹 있지만, 서비스 초기에는 이 문제가 중요하지 않다고 판단하여 그 빈도가 낮기 때문에 추가 조치를 취하지 않았습니다.
AWS Lambda cold start 문제 #
위의 테스트 결과에서 볼 수 있듯이, 95백분위 응답 시간이 30초를 넘었습니다. 이는 서비스 경험에 부정적인 영향을 미쳤습니다. 람다의 특성상 인스턴스가 약 10분 후에 초기화되며, 다음 호출은 함수가 실행될 새 환경을 설정합니다. 이때 모델 이미지 크기가 2.4GB로 크기 때문에 Amazon ECR에서 로드하고 실행하는 데 시간이 오래 걸렸습니다. 이를 cold start 문제라고 합니다.
첫 번째로 시도한 것은 AWS 람다에서 제공되는 용량을 미리 구성하는 것이었습니다. 제공된 용량은 람다 함수에 사전 초기화된 실행 환경의 수이며, 이러한 실행 환경은 들어오는 함수 요청에 즉시 응답할 준비가 되어 있습니다. 단점은 비용이 많이 든다는 것이므로, 저희는 다른 방법을 고려하였습니다. 두 번째로 시도한 방법은 람다 함수를 직접 호출하여 계속 실행되게 하는 것이었습니다. AWS EventBridge를 사용하여 Cronjob을 생성하여 함수를 매분 트리거하여 활성 상태를 유지하고, 결과적으로 50백분위 응답 시간은 약 0.25초이고 95백분위 응답 시간은 3초로 줄었습니다.
애플리케이션 시연 #
저희의 프론트엔드 애플리케이션은 다양한 이점을 제공하는 Flutter 프레임워크를 사용하여 설계 및 구축되었습니다. 우선 저희 서비스의 성격을 고려할 때 모바일 애플리케이션으로 제공하는 것이 웹 기반 솔루션보다 적합하다고 판단했습니다. 앱은 학생들이 수학 문제의 이미지를 캡처할 수 있도록 요구하므로, 모바일 인터페이스는 사용의 용이성과 실용성 측면에서 이상적인 선택입니다.
둘째로, 저희 서비스의 접근성을 높이기 위해 Flutter를 선택했습니다. Flutter는 크로스플랫폼 프레임워크로, iOS 및 Android 플랫폼 모두에서 동시에 애플리케이션을 개발할 수 있습니다. 이러한 광범위한 호환성은 학생들이 사용하는 스마트폰의 종류에 관계없이 저희 서비스를 다양한 학생들에게 제공할 수 있게 합니다.
다른 하이브리드 모바일 앱 개발 프레임워크인 React Native와 비교할 때, Flutter는 저희 앱이 수학 문제의 이미지 캡처에 의존하고 있기 때문에 중요한 기능인 카메라 플러그인을 더 잘 지원한다는 점에서 돋보였습니다.
저희 서비스의 사용방법은 매우 간단합니다. 학생이 수학 문제의 사진을 찍고 확인 버튼을 누르면, 애플리케이션은 이미지를 사용하여 문제를 식별하고 같은 주제나 챕터 내에서 비슷한 문제를 찾아 이러한 비슷한 문제를 학생에게 제공합니다. 이 방식을 통해 학생들은 자신이 어려워하는 문제와 유사한 문제를 해결함으로써 주제에 대한 이해와 숙달을 향상시킬 수 있습니다.
현재 애플리케이션은 데모 단계에 있으며 주로 문제 식별 및 추천 기능을 수행합니다. 그러나 향후 개선 사항에는 더 개인화된 기능과 비슷한 문제들의 대량 추천이 포함될 것이며, 이를 통해 사용자들의 학습 경험을 향상시킬 것입니다. 저희 애플리케이션의 확장 가능성과 추가적인 개선 가능성은 매우 크며, 저희 서비스를 지속적으로 개선해 나갈 것을 기대하고 있습니다.
결론 #
고등학교 수학 문제 추천 서비스를 구축하기 위해, 저희는 다음과 같은 작업을 수행했습니다:
- 한국 수학 문제를 수집하기 위한 효율적인 파이프라인 구축
- ko-SRoBERTa를 사용하여 한국 수학 문제 임베딩 모델 훈련
- 비싼 GPU 인스턴스 없이 AWS 서비스를 사용하여 벡터 검색 기반의 추천 시스템 구축
- 학생들이 쉽게 접근할 수 있는 스마트폰 앱 프론트엔드를 Flutter로 개발
이러한 작업을 통해, 저희는 고등학생들이 수학 학습 여정을 돕는 포괄적이고 효율적인 서비스를 만드는 것을 목표로 했습니다.
향후 작업 #
여러 프로젝트 중간 리뷰, 데모 데이 및 저희 경험을 통해 받은 피드백을 바탕으로, 향후 필요할 것으로 생각되는 다음과 같은 작업들을 고려하고 있습니다:
비즈니스 관점 #
- 사용자 참여 증가 방법은?
받은 피드백 중 하나는 저희 서비스가 기술적으로 탄탄하지만 사용자 참여 측면에서는 부족하다는 것이었습니다. 이는 데모 데이 동안 다른 팀들에 비해 저가의 코인을 압도적으로 많이 받은 것에서도 명확히 드러났습니다.
그러므로 향후 고등학생들의 학습 여정을 진정으로 도울 수 있는 기능이 무엇인지 고려할 필요가 있으며, 서비스의 상업적인 관점에서 가능성을 탐색해야 합니다.
- 좋은 추천이란 무엇인가?
여러 검토 세션 동안 반복해서 받은 질문은 “좋은 문제 추천이란 무엇인가?“입니다. 좋은 추천에 대한 선호도는 개인에 따라 다를 수 있기 때문에, 저희의 추천 정책을 지속적으로 개선하는 것이 중요합니다. 이를 해결하기 위해, 저희는 문제 추천 페이지에 사용자 피드백 버튼을 도입했습니다. 이를 통해 사용자들은 받은 추천에 대한 피드백을 제공할 수 있으며, 이는 저희가 추천 알고리즘을 개선하고 향상시키는 데 도움이 될 것입니다.
기술적 관점 #
- 추천의 지연 시간
현재 스마트폰 애플리케이션에서 추천된 문제를 가져오는 데 약 3-4초가 걸립니다. 이에는 두 가지 주요 원인이 있습니다: 1) 비효율적인 내부 API 호출 절차, 2) 큰 모델 매개변수 크기. 이를 해결하기 위해, 비효율적인 호출 절차를 더 비동기적으로 재구조화하고 지연 시간을 줄일 수 있는 다양한 경량 기술을 탐색하여 사용자 경험을 크게 개선할 수 있습니다.
- 확장 가능한 벡터 검색
현재 시스템은 사용자의 입력 문제의 임베딩 벡터와 데이터베이스의 모든 임베딩 벡터 간의 코사인 유사성을 계산하여 상위 3개 문제를 검색합니다. 그러나 이 k-NN 벡터 검색 구현은 계산적으로 비효율적일 수 있습니다. faiss나 annoy와 같은 근사 최근접 이웃(ANN) 검색을 제공하는 라이브러리를 사용함으로써, 문제 데이터셋이 대규모로 확장될 때에도 거의 동일한 응답 속도를 유지할 수 있습니다.
- 단원/난이도 분류 모델
시간 제약으로 인해 현재는 문제의 소단원/난이도 수준을 예측하기 위해 미세 조정된 ChatGPT API를 사용하고 있습니다. 그러나 정확도는 약 80%로 개선이 필요합니다. 내부 실험에 따르면 문제 임베딩을 기반으로 모델을 구축하면 더 나은 예측 모델을 만들 수 있습니다. 실험 로그에 있는 (x_cls_acc) 플롯은 문제 임베딩 모델 후 소단원 분류 모델을 훈련할 때의 정확도를 보여줍니다. 훈련 초기 단계에서 정확도가 크게 향상되므로 이 분야에서의 추가 개발 가능성이 있습니다.
광범위한 영향 #
1. 저작권 문제 #
저희 프로젝트에서는 수학 문제를 수집하고 제공하며, 이는 저작권 문제에 주의를 기울일 필요가 있습니다. 일부 악의적인 사용자가 추천된 문제를 수집하여 저희의 콘텐츠를 사용해 저작권을 침해하는 방식으로 저희 문제를 배포할 수 있습니다. 따라서 사용자가 저희 서비스를 올바르게 사용하는 방법에 대해 충분한 지도를 제공하는 것이 중요합니다.
무엇보다도 저희는 문제의 출처와 저작권을 인지해야 합니다. 필요한 경우 저작권에 관한 협약이나 허가를 구하고 적절한 출처 표기를 통해 저작권 침해를 방지해야 합니다. 이를 통해 저희 프로젝트는 저작권 문제를 적절히 해결하고자 합니다.
2. 사용자 데이터 수집 #
저희 프로젝트에서는 사용자가 캡처한 문제를 수집하여 데이터베이스에 추가한다는 정책을 결정했습니다. 이는 개인 데이터 보호와 관련된 우려를 일으킬 수 있습니다. 저희는 사용자의 개인 정보를 존중하고 데이터 보호 법률 및 관련 규정을 준수해야 합니다.
따라서 사용자 데이터를 수집할 때는 명확한 동의 절차를 따르고 적절한 보안 조치를 구현하여 데이터의 기밀성과 보호를 보장해야 합니다. 또한, 수집된 데이터의 무단 공개나 부적절한 사용을 자제해야 합니다. 사용자의 신뢰를 유지하고 개인 정보를 적절히 관리함으로써, 저희 프로젝트는 사회적 영향을 강화할 수 있다고 믿고 있습니다.