[TS] MelLearn 프로젝트 퀴즈 정확도 개선기

MelLearn 프로젝트에서 발생했던 정확도 문제와 프롬프트 개선기
정찬's avatar
Aug 22, 2024
[TS] MelLearn 프로젝트 퀴즈 정확도 개선기

개요

MelLearn은 음악을 활용한 언어 학습 퀴즈 서비스입니다. GPT 기반 LLM으로 퀴즈를 자동 생성했지만, 초기 정확도는 단 10%에 불과했습니다.
이 문제를 해결하기 위해 다양한 프롬프트 엔지니어링 기법을 학습하고 적용했으며, 한 달간의 반복적인 개선을 통해 85%까지 정확도를 끌어올렸습니다.
 
당시 발생했던 문제와 해결방안을 프롬프트의 변화와 함께 공유하고자 합니다.

환경

사용 모델: GPT-3.5-turbo-0125

GPT-4 vs GPT-3.5 트레이드오프

GPT-4 장점:
  • 한국어 처리 성능 향상
  • 포맷 일관성 개선
  • 추가 정확도 상승 가능
GPT-3.5 선택 이유:
  • 비용 효율성: GPT-4 대비 1/10 수준
  • 운영 지속성: 대규모 문제 생성에 적합
GPT-3.5 모델 선택:
개발 도중에 성능이 향상된 GPT4 모델이 출시됐지만 호출 비용이 10배정도 비쌌습니다. 그래서 GPT 3.5 모델은 유지하고, 프롬프트 엔지니어링을 통해 정확도를 개선하는 방향으로 결정했습니다.

요구사항 분석

LLM을 통해 사용자의 퀴즈 카테고리와 난이도에 알맞는 4지선다의 퀴즈를 생성해야 합니다.
퀴즈는 4가지 (어휘, 문법, 독해, 듣기) 영역에서 출제됩니다. 학습 난이도는 3가지로(초급, 중급, 고급) 나누어져 있습니다.

요구사항에 맞는 퀴즈 예시

# Vocabulary Quiz, 난이도 - 하 Q. 다음 빈칸에 들어갈 단어로 알맞은 것은? "I've been reading books of old The legends and the myths The moon and its ___", 1. eclipse 2. furniture 3. passport 4. sandwich A. eclipse (정답과 해설)

초기 프롬프트의 문제점

주요 문제점

  1. 난이도 구분 불명확 - 레벨별 퀴즈 차이가 미미
  1. 카테고리 간 구분 부족 - 단어 퀴즈와 읽기 퀴즈가 유사하게 생성
  1. 출력 포맷 불일치 - JSON 형식이 일관되지 않아 파싱 오류 빈발
이러한 문제의 근본 원인은 모호한 지시문지시문들의 충돌(너무 많은 지시문 양) 이었습니다.

해결 방안

기존 프롬프트와 개선된 프롬프트를 AS-IS / TO-BE 형태로 비교해 설명드리겠습니다.
예시를 위해 한국어로 번역한 프롬프트를 사용한 것입니다. 실제 환경에서는 영문으로 작성된 프롬프트를 사용했습니다.

1. 프롬프트 분할 (Prompt Splitting)

문제점

하나의 프롬프트에 모든 요구사항을 담으면 다음과 같은 문제가 발생합니다.
  • 토큰 수 증가 → 비용 상승
  • 지시문 충돌 → 상호 모순되는 지시사항 발생
  • 유지보수 어려움 → 수정 시 전체 프롬프트 재분석 필요

해결 방법

  • 단일 책임 원칙(SRP)**을 적용하여 프롬프트를 작업 단위로 분할했습니다.
변경 전 디렉토리 구조
- prompt/ └── prompt.txt
변경 후 디렉토리 구조
- prompt/ ├── listening/ │ └── listening.txt ├── grammar/ │ └── grammar.txt ├── reading/ │ └── reading.txt └── vocabulary/ ├── beginner.txt ├── intermediate.txt └── advanced.txt
효과: 카테고리 간 혼동과 난이도 모호성이 크게 줄어들었습니다.

2. 프롬프트 엔지니어링 기법 적용

2-1. 명확하고 구체적인 지시 제공

프롬프트의 정확도가 떨어진다면, 대부분은 지시문의 모호성을 구체화함으로써 해결할 수 있습니다. 이는 가장 쉽고 강력한 프롬프트 기법 중 하나입니다.

AS-IS

# prompt 노래 가사를 기반으로 빈칸 채우기 퀴즈를 만들어줘
위의 예시는 당연하게도 모호하다는 문제가 있습니다. 하지만 이 모호한 지시문이 모호한 결과를 만드느냐? 는 LLM의 스펙에 따라 다르겠죠..
실제로 당시 GPT 3.5 turbo 모델은 Fill-in-the-blank question(빈칸 채우기 문제) 의 개념은 학습했었습니다만, 우리의 요구사항들과 fit한 퀴즈를 출력하지 못했습니다.
AI가 학습한 개념을 토대로 모호했던 요구사항(퀴즈의 포맷 결과적으로, 요구사항에 대한 추가 지시를 추가할수록, 모델이 학습했던 빈칸 채우기 문제의 개념이 뒤바뀌게 되었고 빈칸 채우기 퀴즈를 A to Z까지 지시하도록 결정합니다.

TO-BE

당신이 따라야 할 퀴즈 제작 규칙은 다음과 같습니다: 퀴즈는 반드시 **동사(verb), 명사(noun), 형용사(adjective)**의 의미를 묻는 질문이어야 합니다. 총 5개의 퀴즈를 출제해야 합니다. Question Format 문제에는 반드시 사용자가 제공한 문장의 인용구가 포함되어야 합니다. 퀴즈의 질문은 반드시 가사 문장을 포함해야 하며, 모든 'Question'은 한국어로 작성되어야 합니다. 문제에 인용되는 문장은 반드시 의미 있는 문장이어야 합니다. OptionList Format OptionList는 반드시 사용자가 제공한 언어 형식(lang)에 따라 해당 외국어로 작성되어야 합니다. 만약 사용자가 lang: en을 제공했다면, 보기 목록은 영어로 작성되어야 합니다. OptionList는 짧은 문장 형태를 사용하세요. 보기는 한국어가 아닌 외국어로 작성되어야 함을 명심하세요. Comment Format Comment는 반드시 한국어로 해설을 작성해야 합니다.
비교를 위해 가져온 예시긴 하지만, 문장 수가 많아졌죠?
왜냐하면 LLM은 8살 정도 된 어린 아이와 같아서 정확하고 이해하기 쉬운 지시를 해야합니다.
"When a child is struggling, you must ask the question in a way the child will understand… It is the exact same for artificial intelligence."
위는 Michael Taylor라는 프롬프트 전문가가 한 말입니다. 직역해보면 "아이가 어려움을 겪고 있을 때는, 그 아이가 이해할 수 있는 방식으로 질문을 해야 한다… 인공지능도 마찬가지다." 라는 뜻을 갖고 있습니다.

"구체화"는 정해진 정도가 없다.

"구체화" 기법은 도입하기 쉽고 강력한 효과가 있다는 장점이 있습니다만.. 많은 시간이 필요하다는 단점이 존재합니다.
예를 들어, 한 번에 효과가 드러나는 "One-Shot" 이나 "Step-by-Step" 방식과는 달리, "구체화" 기법은 모호한 표현을 반복적으로 찾아내고 점진적으로 개선해 나가는 과정이 핵심입니다. 특히 요구사항이 많은 프롬프트일수록 이 반복 과정이 더욱 중요합니다.

2-2. 마크다운 문법 활용

LLM은 마크다운 문법을 학습해서 구조를 이해하고 있다고 합니다. 따라서 프롬프트 내에 # 제목, - 리스트, 1. 순서 등을 사용하여 구조를 명확하게 할 수 있습니다.

AS-IS

Question Format 문제에는 반드시 사용자가 제공한 문장의 인용구가 포함되어야 합니다. 퀴즈의 질문은 반드시 가사 문장을 포함해야 하며, 모든 'Question'은 한국어로 작성되어야 합니다. 문제에 인용되는 문장은 반드시 의미 있는 문장이어야 합니다. OptionList Format OptionList는 반드시 사용자가 제공한 언어 형식(lang)에 따라 해당 외국어로 작성되어야 합니다. 만약 사용자가 lang: en을 제공했다면, 보기 목록은 영어로 작성되어야 합니다. OptionList는 짧은 문장 형태를 사용하세요. 보기는 한국어가 아닌 외국어로 작성되어야 함을 명심하세요. Comment Format Comment는 반드시 한국어로 해설을 작성해야 합니다.
인간이 봤을 때도 글의 구조 파악이 힘들죠? 어떤 항목에 대한 지시문인지 판단하는데 혼동이 발생합니다.

TO-BE

## Question Format - 문제에는 반드시 사용자가 제공한 문장의 인용구가 포함되어야 합니다. 퀴즈의 질문은 반드시 가사 문장을 포함해야 하며, 모든 'Question'은 한국어로 작성되어야 합니다. - 문제에 인용되는 문장은 반드시 의미 있는 문장이어야 합니다. ## OptionList Format - OptionList는 반드시 사용자가 제공한 언어 형식(lang)에 따라 해당 외국어로 작성되어야 합니다. - 만약 사용자가 lang: en을 제공했다면, 보기 목록은 영어로 작성되어야 합니다. - OptionList는 짧은 문장 형태를 사용하세요. - **보기는 한국어가 아닌 외국어로 작성되어야 함을 명심하세요.** ## Comment Format - Comment는 반드시 한국어로 해설을 작성해야 합니다.
제목(##), 항목(-) 강조( ** ) 와 같은 마크다운 문법을 사용해서 글의 구조를 정리했습니다.

2-3. Few-shot Prompting

Few-shot prompting은 몇 개의 예시(example) 를 함께 제시하여 모델이 문제의 패턴이나 작업 방식을 학습하도록 유도하는 방식입니다. 즉, 모델에게 "이런 식으로 응답해"라고 알려주는 사례 기반 학습 기법입니다.
이것도 가장 쉬우면서 강력했던 방법중 하나였습니다.

AS-IS

## Question Format - 문제에는 반드시 사용자가 제공한 문장의 인용구가 포함되어야 합니다. 퀴즈의 질문은 반드시 가사 문장을 포함해야 하며, 모든 'Question'은 한국어로 작성되어야 합니다. - 문제에 인용되는 문장은 반드시 의미 있는 문장이어야 합니다. ## OptionList Format - OptionList는 반드시 사용자가 제공한 언어 형식(lang)에 따라 해당 외국어로 작성되어야 합니다. - 만약 사용자가 lang: en을 제공했다면, 보기 목록은 영어로 작성되어야 합니다. - OptionList는 짧은 문장 형태를 사용하세요. - **보기는 한국어가 아닌 외국어로 작성되어야 함을 명심하세요.** ## Comment Format - Comment는 반드시 한국어로 해설을 작성해야 합니다.
위 프롬프트에서 모호한 문장이 발견된다면, 구체화를 통해 대부분 해결할 수 있습니다.
하지만 구체화만으로는 해결되지 않는 문제도 존재합니다. 구체화를 반복하다 보면 결국 AI 모델과 사람 간의 ‘진짜 대화’에 가까운 형태로 발전하게 됩니다. 그러나 AI 모델은 실제 인간과 달리, 문맥을 완전히 이해하거나 의도를 깊이 파악하지 못하는 한계가 있습니다.
아래 예시를 봐봅시다.
문제에 인용되는 문장은 반드시 의미 있는 문장이어야 합니다.
  • *"의미 있는 문장"**이라는 지시문은 AI 모델이 제대로 이해하지 못할 수 있습니다. 그렇다면 어떻게 하면 AI가 의미 있는 문장과 의미 없는 문장을 구분할 수 있도록 도와줄 수 있을까요?
예를 들어, "Oh Oh Oh Oh"처럼 반복되는 후렴구는 의미 없는 문장의 대표적인 사례입니다. 사실 위 문장에서 이미 답이 나와 있습니다. 저 역시 **"의미 있는 문장"**을 설명하기 위해 "Oh Oh Oh Oh"라는 예시를 들어 대조적으로 설명했으니까요.
AI도 마찬가지입니다. 모델에게 지시문만 던지는 것보다, 구체적인 예시를 함께 제공하면 모호했던 지시문도 사례 기반 학습을 통해 자연스럽게 구체화된 효과를 낼 수 있습니다.

TO-BE

## Question Format - 문제에는 반드시 사용자가 제공한 문장의 인용구가 포함되어야 합니다. 퀴즈의 질문은 반드시 가사 문장을 포함해야 하며, 모든 'Question'은 한국어로 작성되어야 합니다. - 문제에 인용되는 문장은 반드시 의미 있는 문장이어야 합니다. e.g) meaningful Question : "'You best believe I'm yours'이 문장에서 'believe'의 의미로 가장 가까운 것을 고르세요. e.g) meaningless Question : 'Do, do, do, do you, but do you, do you, do, do, but do you want to come on' 이 문장에서 'want'의 의미는 무엇입니까?
또한, 예시를 제공하는 기법은 역직렬화 과정에서 발생할 수 있는 파싱 문제를 완화하는 데도 효과적입니다.

출력 포맷 예시를 통해 역직렬화 파싱 문제 해결

### 퀴즈 출력 포맷 e.g) {"probList":[{"question":"'Very unmanageable day' What is the meaning of 'unmanageable' in this sentence?","optionList":["A situation or element that is impossible to manage or resolve, presenting significant difficulties as it defies standard approaches or interventions.","An arrangement or timetable that has not been organized effectively, leading to conflicts or disruptions due to poor coordination and planning.","An object or individual that is notably bigger than what is typically expected, making it stand out because of its considerable size or stature.", "A place or time characterized by a significant absence of sunlight, resulting in darker, gloomier conditions that affect the overall ambiance."],"word":"unmanageable","answer":"1","comment":"'Very unmanageable day'에서 'unmanageable'은 일상의 사건이나 상황이 너무 복잡하거나 어려워서 쉽게 다루거나 제어할 수 없을 때 사용되는 형용사입니다. 이 경우, 'unmanageable'은 그 날이 너무 혼란스럽거나 예측할 수 없는 상황들로 가득 차서 일상적인 관리나 대처가 힘든 상태를 의미합니다. 선택지 B, C, D는 이 문맥에서 'unmanageable'의 의미와 직접적으로 관련이 없습니다. B는 일정이 제대로 계획되지 않았음을, C는 물리적 크기와 관련된 의미를, D는 날씨 상태와 관련된 의미를 나타내지만, 이들은 'unmanageable'이 표현하고자 하는 복잡하고 제어하기 어려운 상황의 본질과는 거리가 있습니다. 따라서 정답 A는 이 구절에서 'unmanageable'이 의미하는 바를 가장 잘 반영합니다."}, ... four more quizzes]}
이런식으로 Json 방식을 출력 포맷으로 지정하여 역직렬화 과정에서 파싱 중 오류를 완화시킬 수 있습니다.

정량적 성과 측정

테스트 방법

  • 참여자: 팀원 4명
  • 기간: 4주간 (시험 기간 제외)
  • 주간 문항: 각자 80문제씩 풀이 (4유형 × 20문제)
  • 총 문항: 1,280문제

주차별 개선 결과

주차
문항 수
오류 수
정확도
주요 개선사항
1주차
320
287
10.31%
초기 프롬프트
2주차
320
159
50.31%
프롬프트 분할 + Few-shot
3주차
320
79
75.31%
Vocabulary 난이도별 분할
4주차
320
47
85.31%
Prompt Chaining 도입

주차별 개선 세부사항

1주차 (10% → 50%)
  • 프롬프트 카테고리별 분할
  • Few-shot Prompting 적용
  • 마크다운을 도입하여 기본적인 지시문 구조 개선
2주차 (50% → 75%)
  • Vocabulary 유형 난이도별 세분화
  • 지시문 구체화 작업
  • 출력 포맷 일관성 향상
3주차 (75% → 85%)
  • Listening 유형 단어 선정 기준 명확화
  • Listening 빈칸 위치 로직 개선

한계와 개선 방향

85%에서 멈춘 이유

구조적 한계:
  • LLM 할루시네이션: 존재하지 않는 단어/문장 생성
  • GPT-3.5 다국어 한계: 한국어 + 영어 혼합 처리 불안정
  • 토큰 길이 제한: 복잡한 지시문 처리 한계

느낀점

LLM은 마법을 부리는 것이 아니다.

이번 프로젝트를 통해 가장 크게 깨달은 점은 "LLM이 알아서 잘 해주겠지"라는 막연한 기대가 얼마나 위험한지였다. 처음엔 좋은 모델에 데이터만 넣어주면 자동으로 괜찮은 결과가 나올 거라고 생각했는데, 실제로는 전혀 그렇지 않았다.

내가 겪었던 프롬프트 엔지니어링이란…

프롬프트 엔지니어링은 LLM에게 적은 토큰으로 효율적으로 지시하는 방법이라고 정의하고 싶다.
효율적인 지시 방식은 학습을 통해 익힐 수 있다. 예를 들어, LLM이 마크다운 문법을 이해하고 있다는 점을 활용하거나, 예시를 제공해 학습을 돕는 등의 방법은 지식 기반의 방법론에 해당한다.
하지만 지시문을 구체화하는 과정은 커뮤니케이션의 영역이라고 생각한다. 상대방이 모르는 것을 이해할 수 있도록 설명하는 과정은 실제 사람 간의 커뮤니케이션에서도 자주 일어나는 일이다.
결국 프롬프트 엔지니어링에서는 세 가지 요소가 유기적으로 맞물려야 한다는 것을 배웠다.
  1. 모델이 이해할 수 있도록 정보를 구조화하는 것,
  1. 복잡한 문제를 모델이 처리할 수 있는 단위로 쪼개는 것,
  1. 반복적인 테스트와 결과 분석을 통해 개선해 나가는 것.
이 중 하나라도 빠지면 원하는 품질의 결과를 얻기 어려웠다.
Share article

lushlife99